wifi: mt76: mt7925: add NAN security and improve NDP reliability (part 2)
Subsystems (2)
cover wifi: mt76: mt7925: add NAN security and improve NDP reliability (part 2)
This is part 2 of the MT7925 NAN work. It continues the 15-patch series that fixed NAN operation and NDP performance:
https://lore.kernel.org/all/[email protected]/
This series adds NAN MAC address randomization, low power event control, and key management for secure NAN and NAN data interfaces. It also makes unicast management TX respect peer availability and discovery windows, and fixes shared BSS teardown, cluster event ordering, and device removal and frame lifetime issues found during NDP setup and teardown.
The 23 patches here cover the mt76 follow-up work. The separate mac80211 NAN receive-filter change is not included in this series.
Chengwei Yu (10):
wifi: mt76: mt7925: make NMI address TLV tail padding explicit
wifi: mt76: mt7925: add NMI/NDI MAC address setter helpers
wifi: mt76: mt7925: implement NAN MAC address randomization
wifi: mt76: mt7925: wire up NAN MAC randomization lifecycle
wifi: mt76: mt7925: add NAN low power event control on suspend/resume
wifi: mt76: mt7925: implement NAN key management MCU command
wifi: mt76: mt7925: add per-peer RX IGTK/BIGTK and RX GTK WTBLs
wifi: mt76: mt7925: add NDC-aware TX GTK table for NAN_DATA
wifi: mt76: mt7925: implement mt7925_nan_set_key for NAN security
wifi: mt76: mt7925: install NAN BIP keys standalone in sta_key_tlv
Jacobs Wu (13):
wifi: mt76: mt7925: do not disable RX NAPI twice on unload
wifi: mt76: mt7925: assign the interface WTBL to the NAN management
TXQ
wifi: mt76: mt7925: do not deactivate shared NAN BSS on peer STA
removal
wifi: mt76: gate NAN unicast mgmt by peer availability via STA queues
wifi: mt76: mt7925: hold NAN mgmt TX to the discovery window
wifi: mt76: mt7925: double the retry budget for NAN unicast management
wifi: mt76: mt7925: let TX status of DW-held NAN frames outlive the DW
period
wifi: mt76: mt7925: defer the NAN joined-cluster event out of
NAN_START
wifi: mt76: mt7925: steer NAN handshake frames by committed-bitmap
state
wifi: mt76: mt7925: disable only the RX NAPI instances that exist
wifi: mt76: mt7925: stop queueing resets once the device is being
removed
wifi: mt76: mt7925: bound the lifetime of NAN unicast management
frames
wifi: mt76: mt7925: always deliver the joined-cluster event through
the deferred work
drivers/net/wireless/mediatek/mt76/mt76.h | 1 + .../net/wireless/mediatek/mt76/mt7925/mac.c | 23 + .../net/wireless/mediatek/mt76/mt7925/main.c | 147 +++- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 30 +- .../net/wireless/mediatek/mt76/mt7925/mcu.h | 11 + .../wireless/mediatek/mt76/mt7925/mt7925.h | 3 + .../net/wireless/mediatek/mt76/mt7925/nan.c | 710 +++++++++++++++++- .../net/wireless/mediatek/mt76/mt7925/nan.h | 131 ++++ .../net/wireless/mediatek/mt76/mt7925/pci.c | 19 +- .../wireless/mediatek/mt76/mt7925/pci_mac.c | 58 +- drivers/net/wireless/mediatek/mt76/mt792x.h | 98 +++ .../net/wireless/mediatek/mt76/mt792x_core.c | 34 +- drivers/net/wireless/mediatek/mt76/tx.c | 25 +- 13 files changed, 1248 insertions(+), 42 deletions(-)
Signature · 1 line
2.43.0
PATCH 1/23 wifi: mt76: mt7925: make NMI address TLV tail padding explicit
From: Chengwei Yu <[email protected]>
struct mt7925_nan_nmi_addr_tlv is __packed __aligned(4). tag+len+ nmi_addr is 10 bytes, so the aligned attribute already inserts 2 bytes of implicit tail padding to round the struct up to 12 bytes; sizeof() is unchanged by this patch.
Every sibling NAN TLV struct in this file (cluster_id, sync_rssi, peer_rec, map_sta_rec, ...) declares its tail padding as an explicit reserved[] member instead of relying on implicit compiler padding. Make mt7925_nan_nmi_addr_tlv consistent with that convention, and give the field a name so it shows up (zeroed) in TLV dumps instead of appearing as anonymous struct padding.
--- drivers/net/wireless/mediatek/mt76/mt7925/nan.h | 1 + 1 file changed, 1 insertion(+)
drivers/net/wireless/mediatek/mt76/mt7925/nan.h +1 -0
@@ -346,6 +346,7 @@ struct mt7925_nan_nmi_addr_tlv {__le16 tag;__le16 len;u8 nmi_addr[ETH_ALEN ]; +u8 reserved[2 ]; } __packed__aligned(4) ;struct mt7925_nan_avail_ctrl_tlv {
Signature · 1 line
2.43.0
PATCH 2/23 wifi: mt76: mt7925: add NMI/NDI MAC address setter helpers
In reply to Sean WangFrom: Chengwei Yu <[email protected]>
Add mt7925_nan_set_nmi_address() and mt7925_nan_set_ndi_address() for runtime MAC address changes: NMI goes through the MCU (firmware owns the hardware address), NDI updates vif->addr directly.
--- .../net/wireless/mediatek/mt76/mt7925/nan.c | 48 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/nan.h | 8 ++++ 2 files changed, 56 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +48 -0
@@ -697,6 +697,54 @@ void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb) } } +int mt7925_nan_set_nmi_address (struct ieee80211_vif *vif, +struct mt792x_dev *dev, +const u8 *mac_address) +{ +struct mt76_dev *mdev = &dev->mt76; +struct { +u8 rsv[4 ]; +struct mt7925_nan_nmi_addr_tlv nmi_addr_tlv; + } nmi_cmd = { + .nmi_addr_tlv = { + .tag =cpu_to_le16 (NAN_UNI_CMD_CHANGE_NMI_ADDRESS ), + .len =cpu_to_le16 (sizeof (struct mt7925_nan_nmi_addr_tlv )), + }, + }; + +if (!dev || !vif || !mac_address) +return -EINVAL ; + +if (is_zero_ether_addr (mac_address) || +is_multicast_ether_addr (mac_address)) +return -EINVAL ; + +memcpy (nmi_cmd.nmi_addr_tlv.nmi_addr, mac_address,ETH_ALEN ); + +return mt76_mcu_send_msg (mdev,MCU_UNI_CMD (NAN ), &nmi_cmd, +sizeof (nmi_cmd), true); +} + +int mt7925_nan_set_ndi_address (struct ieee80211_vif *vif, +struct mt792x_dev *dev, +const u8 *mac_address) +{ +if (!dev || !vif || !mac_address) +return -EINVAL ; + +if (is_zero_ether_addr (mac_address) || +is_multicast_ether_addr (mac_address)) +return -EINVAL ; + +/* +* The NDI address is not managed by firmware; it is the vif's own +* hardware address, so updating it locally is sufficient. +*/ +memcpy (vif->addr, mac_address,ETH_ALEN ); + +return 0 ; +} +static int mt7925_nan_avail_ctrl_tlv (struct sk_buff *skb,struct ieee80211_vif *vif) {
drivers/net/wireless/mediatek/mt76/mt7925/nan.h +8 -0
@@ -495,6 +495,14 @@ int mt7925_nan_change_configure(struct ieee80211_vif *vif,void mt7925_nan_mcu_event (struct mt792x_dev *dev,struct sk_buff *skb); +int mt7925_nan_set_nmi_address (struct ieee80211_vif *vif, +struct mt792x_dev *dev, +const u8 *mac_address); + +int mt7925_nan_set_ndi_address (struct ieee80211_vif *vif, +struct mt792x_dev *dev, +const u8 *mac_address); +void mt7925_nan_local_sched_changed (struct mt792x_dev *dev,struct ieee80211_vif *vif);
Signature · 1 line
2.43.0
Felix Fietkau
maintainerRe: [PATCH 02/23] wifi: mt76: mt7925: add NMI/NDI MAC address setter helpers
Quoted 56 lines · Sean Wang
From: Chengwei Yu <[email protected]>
Add
mt7925_nan_set_nmi_address()andmt7925_nan_set_ndi_address()for runtime MAC address changes: NMI goes through the MCU (firmware owns the hardware address), NDI updatesvif->addrdirectly.Co-developed-by Sean Wang <[email protected]>Signed-off-by Sean Wang <[email protected]>Signed-off-by Chengwei Yu <[email protected]>--- .../net/wireless/mediatek/mt76/mt7925/nan.c | 48 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/nan.h | 8 ++++ 2 files changed, 56 insertions(+)drivers/net/wireless/mediatek/mt76/mt7925/nan.c +34
-0@@ -697,6 +697,54 @@ void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb) } } +int mt7925_nan_set_nmi_address (struct ieee80211_vif *vif, +struct mt792x_dev *dev, +const u8 *mac_address) +{ +struct mt76_dev *mdev = &dev->mt76; +struct { +u8 rsv[4 ]; +struct mt7925_nan_nmi_addr_tlv nmi_addr_tlv; + } nmi_cmd = { + .nmi_addr_tlv = { + .tag =cpu_to_le16 (NAN_UNI_CMD_CHANGE_NMI_ADDRESS ), + .len =cpu_to_le16 (sizeof (struct mt7925_nan_nmi_addr_tlv )), + }, + }; + +if (!dev || !vif || !mac_address) +return -EINVAL ; + +if (is_zero_ether_addr (mac_address) || +is_multicast_ether_addr (mac_address)) +return -EINVAL ; + +memcpy (nmi_cmd.nmi_addr_tlv.nmi_addr, mac_address,ETH_ALEN ); + +return mt76_mcu_send_msg (mdev,MCU_UNI_CMD (NAN ), &nmi_cmd, +sizeof (nmi_cmd), true); +} + +int mt7925_nan_set_ndi_address (struct ieee80211_vif *vif, +struct mt792x_dev *dev, +const u8 *mac_address) +{ +if (!dev || !vif || !mac_address) +return -EINVAL ;
drivers/net/wireless/mediatek/mt76/mt7925/nan.c void mt7925_nan_mcu_event(struct mt792x_dev *dev, struct sk_buff *skb) · line 743
Quoted 10 lines
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +10
-0+ +if (is_zero_ether_addr (mac_address) || +is_multicast_ether_addr (mac_address)) +return -EINVAL ; + +/* +* The NDI address is not managed by firmware; it is the vif's own +* hardware address, so updating it locally is sufficient. +*/ +memcpy (vif->addr, mac_address,ETH_ALEN );
What about bss_conf->addr and the netdev addr. Do those need to be updated?
- Felix
PATCH 3/23 wifi: mt76: mt7925: implement NAN MAC address randomization
In reply to Sean WangFrom: Chengwei Yu <[email protected]>
Add mt7925_nan_start/stop_mac_randomization() using a per-vif delayed_work and period stored in struct mt792x_nan.
Per-vif (not per-dev) because the NAN iface combinations allow up to two concurrent NAN_DATA vifs; a single dev-wide field would silently lose randomization for the second NDI or let it overwrite the first vif's pending work.
delayed_work (not timer_list) because the work function calls mt7925_nan_set_nmi/ndi_address() which invokes mt76_mcu_send_msg() with wait_resp=true; that can sleep, which is not allowed in softirq context. This is consistent with scan_work, mlo_pm_work and ps_work in this driver.
stop_mac_randomization() must be called without holding the dev mutex since the work function acquires it. No caller is wired up yet; the next patch does that in add/remove_interface.
--- .../net/wireless/mediatek/mt76/mt7925/nan.c | 59 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/nan.h | 7 +++ drivers/net/wireless/mediatek/mt76/mt792x.h | 9 +++ 3 files changed, 75 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +59 -0
@@ -745,6 +745,65 @@ int mt7925_nan_set_ndi_address(struct ieee80211_vif *vif,return 0 ; } +void mt7925_nan_mac_rand_work (struct work_struct *work) +{ + struct mt792x_vif *mvif = container_of(work, struct mt792x_vif, + nan.mac_rand_work.work); +struct mt792x_dev *dev = mvif->phy->dev; +struct ieee80211_vif *vif = + container_of((void *)mvif, struct ieee80211_vif, drv_priv); +bool is_nmi = (vif->type ==NL80211_IFTYPE_NAN ); +u8 addr[ETH_ALEN ]; +int ret; + +mt792x_mutex_acquire (dev); + +if (!mvif->nan.mac_rand_period_sec) + goto out; + +eth_random_addr (addr); + + ret = is_nmi ?mt7925_nan_set_nmi_address (vif, dev, addr) + :mt7925_nan_set_ndi_address (vif, dev, addr); + +if (ret) +dev_err (dev->mt76.dev, +"NAN: failed to randomize %s address: %d\n" , + is_nmi ?"NMI" :"NDI" , ret); + +ieee80211_queue_delayed_work (mt76_hw (dev), &mvif->nan.mac_rand_work, +secs_to_jiffies (mvif->nan.mac_rand_period_sec)); +out: +mt792x_mutex_release (dev); +} + +void mt7925_nan_start_mac_randomization (struct ieee80211_vif *vif, +u32 period_sec) +{ +struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; +struct mt792x_dev *dev = mvif->phy->dev; + +if (!period_sec) +return ; + + mvif->nan.mac_rand_period_sec = period_sec; + +ieee80211_queue_delayed_work (mt76_hw (dev), &mvif->nan.mac_rand_work,0 ); +} + +/* +* Must be called without holding the dev mutex: mt7925_nan_mac_rand_work() +* acquires it, so cancel_delayed_work_sync() here would deadlock against a +* concurrently running instance of that work otherwise. +*/ +void mt7925_nan_stop_mac_randomization (struct ieee80211_vif *vif) +{ +struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + + mvif->nan.mac_rand_period_sec =0 ; +cancel_delayed_work_sync (&mvif->nan.mac_rand_work); +} +static int mt7925_nan_avail_ctrl_tlv (struct sk_buff *skb,struct ieee80211_vif *vif) {
drivers/net/wireless/mediatek/mt76/mt7925/nan.h +7 -0
@@ -503,6 +503,13 @@ int mt7925_nan_set_ndi_address(struct ieee80211_vif *vif,struct mt792x_dev *dev, const u8 *mac_address); +void mt7925_nan_mac_rand_work (struct work_struct *work); + +void mt7925_nan_start_mac_randomization (struct ieee80211_vif *vif, +u32 period_sec); + +void mt7925_nan_stop_mac_randomization (struct ieee80211_vif *vif); +void mt7925_nan_local_sched_changed (struct mt792x_dev *dev,struct ieee80211_vif *vif);
drivers/net/wireless/mediatek/mt76/mt792x.h +9 -0
@@ -181,6 +181,15 @@ struct mt792x_nan {/* Connection index bitmap, up to NAN_MAX_CONN_CFG peers */ unsigned long conn_bitmap; + +/* +* NMI/NDI MAC address randomization. Kept per-vif (rather than a +* single pointer on mt792x_dev) because up to two NAN_DATA vifs can +* coexist (see if_limits_nan_mcc/scc), each needing its own +* independent randomization period and pending work. +*/ +struct delayed_work mac_rand_work; +u32 mac_rand_period_sec; };struct mt792x_vif {
Signature · 1 line
2.43.0
Felix Fietkau
maintainerRe: [PATCH 03/23] wifi: mt76: mt7925: implement NAN MAC address randomization
Quoted 38 lines · Sean Wang
From: Chengwei Yu <[email protected]>
Add mt7925_nan_start/stop_mac_randomization() using a per-vif
delayed_workand period stored in structmt792x_nan.Per-vif (not per-dev) because the NAN iface combinations allow up to two concurrent
NAN_DATAvifs; a single dev-wide field would silently lose randomization for the second NDI or let it overwrite the first vif's pending work.
delayed_work(nottimer_list) because the work function calls mt7925_nan_set_nmi/ndi_address() which invokesmt76_mcu_send_msg()with wait_resp=true; that can sleep, which is not allowed in softirq context. This is consistent withscan_work,mlo_pm_workandps_workin this driver.
stop_mac_randomization()must be called without holding the dev mutex since the work function acquires it. No caller is wired up yet; the next patch does that in add/remove_interface.Co-developed-by Sean Wang <[email protected]>Signed-off-by Sean Wang <[email protected]>Signed-off-by Chengwei Yu <[email protected]>--- .../net/wireless/mediatek/mt76/mt7925/nan.c | 59 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/nan.h | 7 +++ drivers/net/wireless/mediatek/mt76/mt792x.h | 9 +++ 3 files changed, 75 insertions(+)drivers/net/wireless/mediatek/mt76/mt7925/nan.c +3
-0@@ -745,6 +745,65 @@ int mt7925_nan_set_ndi_address(struct ieee80211_vif *vif, +void mt7925_nan_start_mac_randomization (struct ieee80211_vif *vif, +u32 period_sec) +{
drivers/net/wireless/mediatek/mt76/mt7925/nan.c int mt7925_nan_set_ndi_address(struct ieee80211_vif *vif, · line 792
Quoted 10 lines
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +10
-0+struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; +struct mt792x_dev *dev = mvif->phy->dev; + +if (!period_sec) +return ; + + mvif->nan.mac_rand_period_sec = period_sec; + +ieee80211_queue_delayed_work (mt76_hw (dev), &mvif->nan.mac_rand_work,0 ); +}
This function is added, but not used in this series.
- Felix
PATCH 4/23 wifi: mt76: mt7925: wire up NAN MAC randomization lifecycle
In reply to Sean WangFrom: Chengwei Yu <[email protected]>
Start mac_rand_work in mt7925_add_interface() and stop it in mt7925_remove_interface() so the work item added by prior patches is usable and cannot outlive its vif.
INIT_DELAYED_WORK() is unconditional across all vif types, matching how csa_work is already initialized unconditionally -- mvif->nan exists on every mt792x_vif so there is nothing NAN-specific to guard.
mt7925_nan_stop_mac_randomization() is called before mt792x_mutex_acquire(), not inside it: the work function takes the same dev mutex, so cancel_delayed_work_sync() while holding it would deadlock. This follows the same ordering mt792x_unassign_vif_chanctx() uses for csa_work.
--- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +15 -0
@@ -480,6 +480,12 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)if (vif->type ==NL80211_IFTYPE_NAN ) dev->nan_vif = vif; + +if (vif->type ==NL80211_IFTYPE_NAN || + vif->type ==NL80211_IFTYPE_NAN_DATA ) +INIT_DELAYED_WORK (&mvif->nan.mac_rand_work, + mt7925_nan_mac_rand_work); + out:mt792x_mutex_release (dev);
@@ -493,6 +499,15 @@ mt7925_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)struct mt792x_dev *dev =mt792x_hw_dev (hw);struct mt792x_bss_conf *mconf; +/* +* Stop before taking the dev mutex: mt7925_nan_mac_rand_work() +* acquires it too, so cancel_delayed_work_sync() would deadlock +* against a concurrently running instance if called while held. +*/ +if (vif->type ==NL80211_IFTYPE_NAN || + vif->type ==NL80211_IFTYPE_NAN_DATA ) +mt7925_nan_stop_mac_randomization (vif); +mt792x_mutex_acquire (dev);if (dev->nan_vif == vif)
Signature · 1 line
2.43.0
PATCH 5/23 wifi: mt76: mt7925: add NAN low power event control on suspend/resume
In reply to Sean WangFrom: Chengwei Yu <[email protected]>
Disable NAN low power event reporting when the interface suspends so the firmware stops firing peer-schedule notifications into a quiesced system, and re-enable it on resume.
Re-enabling is conditional: it only fires if firmware has previously reported an active peer schedule (non-zero peer_sch_record_tx_map in the LOWPOWER_CTRL event). This avoids sending the command on every resume when no peer connection is in progress.
--- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 9 ++ .../net/wireless/mediatek/mt76/mt7925/nan.c | 93 +++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/nan.h | 18 ++++ drivers/net/wireless/mediatek/mt76/mt792x.h | 7 ++ 4 files changed, 127 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +9 -0
@@ -296,6 +296,7 @@ void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac,bool suspend = !test_bit (MT76_STATE_RUNNING , &phy->state);struct ieee80211_hw *hw = phy->hw;struct cfg80211_wowlan *wowlan = hw->wiphy->wowlan_config; +struct mt792x_dev *dev =mt792x_hw_dev (hw);int i;mt76_connac_mcu_set_gtk_rekey (phy->dev, vif, suspend);
@@ -306,6 +307,14 @@ void mt7925_mcu_set_suspend_iter(void *priv, u8 *mac,mt7925_mcu_set_wow_pattern (phy->dev, vif, i, suspend, &wowlan->patterns[i]);mt7925_connac_mcu_set_wow_ctrl (phy, vif, suspend, wowlan); + +/* +* Disable NAN low power event reporting while suspended, and +* re-enable it on resume so firmware resumes reporting peer +* schedule activity. +*/ +if (vif->type ==NL80211_IFTYPE_NAN ) + mt7925_nan_send_lowpower_ctrl_cmd(dev, !suspend); }#endif /* CONFIG_PM */
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +93 -0
111 lines not on this page · raw message
drivers/net/wireless/mediatek/mt76/mt7925/nan.h +18 -0
@@ -88,6 +88,7 @@ enum nan_uni_cmd_tag {NAN_UNI_CMD_UPDATE_PEER_CAPABILITY =21 ,NAN_UNI_CMD_CHANGE_NMI_ADDRESS =24 ,NAN_UNI_CMD_SET_DW_INTERVAL =26 , +NAN_UNI_CMD_LOWPOWER_CTRL =37 ,NAN_UNI_CMD_SET_SYNC_RSSI =39 ,NAN_UNI_CMD_SET_CLUSTER_ID =40 , NAN_UNI_CMD_KEY_MANAGEMENT = 53,
@@ -95,6 +96,7 @@ enum nan_uni_cmd_tag {enum nan_uni_event_tag {NAN_UNI_EVENT_ID_DE_EVENT_IND =19 , +NAN_UNI_EVENT_LOWPOWER_CTRL =37 ,NAN_UNI_EVENT_ID_ULW_UPDATE =39 ,NAN_UNI_EVENT_ID_SCHED_UPDATE_DONE =43 ,NAN_UNI_EVENT_REPORT_DW_START =59 ,
@@ -476,6 +478,20 @@ struct mt7925_nan_update_phy_setting_tlv {struct mt7925_nan_phy_setting phy_5g; } __packed__aligned(4) ; +/* NAN Low Power Control Command */ +struct mt7925_nan_lowpower_ctrl_tlv { +__le16 tag; +__le16 len; +u8 enabled; +u8 reserved[3 ]; +} __packed__aligned(4) ; + +/* NAN Low Power Control Event */ +struct mt7925_nan_lowpower_ctrl_evt { +u8 peer_sch_record_tx_map; +u8 reserved[7 ]; +} __packed__aligned(4) ; +int mt7925_nan_update_phy_setting (struct mt792x_dev *dev); struct ieee80211_chanctx_conf *
@@ -510,6 +526,8 @@ void mt7925_nan_start_mac_randomization(struct ieee80211_vif *vif,void mt7925_nan_stop_mac_randomization (struct ieee80211_vif *vif); +int mt7925_nan_send_lowpower_ctrl_cmd (struct mt792x_dev *dev,bool enable); +void mt7925_nan_local_sched_changed (struct mt792x_dev *dev,struct ieee80211_vif *vif);
drivers/net/wireless/mediatek/mt76/mt792x.h +7 -0
@@ -182,6 +182,13 @@ struct mt792x_nan {/* Connection index bitmap, up to NAN_MAX_CONN_CFG peers */ unsigned long conn_bitmap; +/* +* Set when firmware reports an active peer schedule via +* NAN_UNI_EVENT_LOWPOWER_CTRL; cleared once the re-enable command +* has been sent on resume. +*/ +bool lowpower_mode; +/* * NMI/NDI MAC address randomization. Kept per-vif (rather than a * single pointer on mt792x_dev) because up to two NAN_DATA vifs can
Signature · 1 line
2.43.0
PATCH 6/23 wifi: mt76: mt7925: implement NAN key management MCU command
In reply to Sean WangFrom: Chengwei Yu <[email protected]>
Add mt7925_nan_manage_key_cmd() and mt7925_nan_key_mgmt_tlv() to install and remove NAN group and pairwise keys via NAN_UNI_CMD_KEY_MANAGEMENT. The TLV builder covers all firmware key
types: NM-TK, ND-TK (unicast), TX/RX GTK (multicast data) and TX/RX IGTK/BIGTK (multicast management).
Follows the same skb + mt76_connac_mcu_add_tlv() pattern as other NAN commands (mt7925_nan_enable(), mt7925_nan_change_configure()) rather than hand-assembling a packed struct, so it composes naturally with the existing NAN command helpers.
Key material is grouped into struct mt7925_nan_key_info so callers pass one pointer instead of five arguments; init/key_exist firmware flags are hardcoded 1/0 since every real caller uses the same values.
--- .../net/wireless/mediatek/mt76/mt7925/nan.c | 122 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/nan.h | 72 +++++++++++ 2 files changed, 194 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +122 -0
@@ -897,6 +897,128 @@ void mt7925_nan_stop_mac_randomization(struct ieee80211_vif *vif)cancel_delayed_work_sync (&mvif->nan.mac_rand_work); } +static int +mt7925_nan_key_mgmt_tlv (struct sk_buff *skb, +enum mt7925_nan_key_operation key_op, +enum mt7925_nan_key_type key_type, +u16 wtbl_entry, +const u8 *local_addr, +const u8 *peer_addr, +const struct mt7925_nan_key_info *key) +{ +struct mt7925_nan_key_mgmt_tlv *key_mgmt_tlv =NULL ; +struct tlv *tlv =NULL ; + +if (!skb) +return -EINVAL ; + + tlv =mt76_connac_mcu_add_tlv (skb,NAN_UNI_CMD_KEY_MANAGEMENT , +sizeof (struct mt7925_nan_key_mgmt_tlv )); +if (!tlv) +return -ENOMEM ; + + key_mgmt_tlv = (struct mt7925_nan_key_mgmt_tlv *)tlv; + key_mgmt_tlv->op = (u8 )key_op; + key_mgmt_tlv->key_type = (u8 )key_type; + key_mgmt_tlv->wtbl_idx =cpu_to_le16 (wtbl_entry); + key_mgmt_tlv->init =1 ; + key_mgmt_tlv->key_exist =0 ; + key_mgmt_tlv->is_nmi_tk =0 ; + +memcpy (key_mgmt_tlv->local_addr, local_addr,ETH_ALEN ); +memcpy (key_mgmt_tlv->peer_addr, peer_addr,ETH_ALEN ); + +switch (key_type) { +case NAN_KEY_TYPE_NMI_CXT_MGMT_KEY : + key_mgmt_tlv->nmi_key_idx =0 ; + key_mgmt_tlv->is_nmi_tk = true; +break ; +case NAN_KEY_TYPE_MC_TX_KEY : +case NAN_KEY_TYPE_MC_MGMT_TX_KEY : +/* TX GTK/IGTK/BIGTK: NDI-indexed slot; NDC ID not yet tracked */ + key_mgmt_tlv->ndi_idx =0 ; +break ; +case NAN_KEY_TYPE_MC_RX_KEY : +case NAN_KEY_TYPE_MC_MGMT_RX_KEY : + key_mgmt_tlv->mc_rx_idx = (u8 )wtbl_entry; +break ; +default : +return -EINVAL ; + } + +if (key_op ==NAN_KEY_OP_SET_KEY ) { + key_mgmt_tlv->algorithm_id = key->algo_id; + key_mgmt_tlv->key_id = key->key_id; + key_mgmt_tlv->key_len = key->key_len; + +if (key_type ==NAN_KEY_TYPE_MC_MGMT_RX_KEY && key->pn) { +memcpy (key_mgmt_tlv->key_rsc, key->pn, +min_t (size_t,NAN_PACKET_NUMBER_LEN , +sizeof (key_mgmt_tlv->key_rsc))); + } + +if (key->key_data) { +memcpy (key_mgmt_tlv->key_material, key->key_data, +min_t (size_t, key->key_len, +sizeof (key_mgmt_tlv->key_material))); + } + } + +return 0 ; +} + +/** +* mt7925_nan_manage_key_cmd - Install/remove a NAN group or pairwise key +* @dev: mt792x device pointer +* @key_op: NAN_KEY_OP_SET_KEY or NAN_KEY_OP_CLS_KEY +* @key_type: which NAN key slot this targets (enum mt7925_nan_key_type) +* @wtbl_entry: WTBL index the key is bound to (WTBL_RESERVED_ENTRY invalid) +* @local_addr: local MAC address for this key context +* @peer_addr: peer MAC address for this key context +* @key: key material; key->key_data required for NAN_KEY_OP_SET_KEY +* +* There are no other callers yet; NAN group key installation added by a +* later change calls into this single command path. +*/ +int +mt7925_nan_manage_key_cmd (struct mt792x_dev *dev, +enum mt7925_nan_key_operation key_op, +enum mt7925_nan_key_type key_type, +u16 wtbl_entry, +const u8 *local_addr, +const u8 *peer_addr, +const struct mt7925_nan_key_info *key) +{ +struct mt76_dev *mdev = &dev->mt76; +struct mt7925_nan_common_hdr *hdr =NULL ; +struct sk_buff *skb =NULL ; +int ret; + +if (!key || !local_addr || !peer_addr || + key_type >=NAN_KEY_TYPE_NUM || + wtbl_entry ==WTBL_RESERVED_ENTRY || + (key_op ==NAN_KEY_OP_SET_KEY && !key->key_data)) +return -EINVAL ; + + skb =mt76_mcu_msg_alloc (mdev,NULL , +sizeof (struct mt7925_nan_common_hdr ) + +sizeof (struct mt7925_nan_key_mgmt_tlv )); +if (!skb) +return -ENOMEM ; + + hdr = (struct mt7925_nan_common_hdr *)skb_put (skb,sizeof (*hdr)); +memset (hdr,0 ,sizeof (*hdr)); + + ret =mt7925_nan_key_mgmt_tlv (skb, key_op, key_type, wtbl_entry, + local_addr, peer_addr, key); +if (ret) { +dev_kfree_skb (skb); +return ret; + } + +return mt76_mcu_skb_send_msg (mdev, skb,MCU_UNI_CMD (NAN ), true); +} +static int mt7925_nan_avail_ctrl_tlv (struct sk_buff *skb,struct ieee80211_vif *vif) {
drivers/net/wireless/mediatek/mt76/mt7925/nan.h +72 -0
90 lines not on this page · raw message
Signature · 1 line
2.43.0
PATCH 7/23 wifi: mt76: mt7925: add per-peer RX IGTK/BIGTK and RX GTK WTBLs
In reply to Sean WangFrom: Chengwei Yu <[email protected]>
NAN RX group keys are per-peer: each NAN peer can carry an independent IGTK/BIGTK, and each NAN_DATA peer an independent GTK. Add nan_rx_igtk_wcid and nan_rx_gtk_wcid to mt792x_sta to hold dedicated WTBL entries for these keys.
WTBLs are allocated lazily: mt7925_nan_sta_wcids_init() marks both fields MT792x_WCID_IDX_UNSET at peer-add time, and mt7925_nan_peer_wcids_free() releases whichever slots were actually allocated at peer-remove time. Peers that never install a NAN group key carry zero overhead.
--- .../net/wireless/mediatek/mt76/mt7925/main.c | 47 +++++++++++++++++++ drivers/net/wireless/mediatek/mt76/mt792x.h | 11 +++++ 2 files changed, 58 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +47 -0
@@ -923,6 +923,8 @@ mt7925_get_rates_table(struct ieee80211_hw *hw, struct ieee80211_vif *vif,return mvif->basic_rates_idx; } +static void mt7925_nan_sta_wcids_init (struct mt792x_sta *msta); + static int mt7925_mac_link_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta,
@@ -1060,6 +1062,9 @@ static int mt7925_mac_link_sta_add(struct mt76_dev *mdev, goto out_pm; } +if (vif->type ==NL80211_IFTYPE_NAN || vif->type ==NL80211_IFTYPE_NAN_DATA ) +mt7925_nan_sta_wcids_init (msta); +mt76_connac_power_save_sched (&dev->mphy, &dev->pm);return 0 ;
@@ -1306,6 +1311,45 @@ int mt7925_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, }EXPORT_SYMBOL_GPL (mt7925_mac_sta_event); +/* Mark per-peer NAN group key WTBLs as unallocated at station add time. +* Pairs with mt7925_nan_peer_wcids_free() at station remove time. +*/ +static void mt7925_nan_sta_wcids_init (struct mt792x_sta *msta) +{ + msta->nan_rx_gtk_wcid.idx = MT792x_WCID_IDX_UNSET; + msta->nan_rx_igtk_wcid.idx = MT792x_WCID_IDX_UNSET; +} + +/* Free a lazily-allocated per-peer NAN group key WTBL and reset its sentinel. */ +static void mt7925_nan_wcid_free (struct mt76_dev *mdev,struct mt76_wcid *wcid) +{ +u16 idx = wcid->idx; + +rcu_assign_pointer (mdev->wcid[idx],NULL ); +mt76_wcid_cleanup (mdev, wcid); +mt76_wcid_mask_clear (mdev->wcid_mask, idx); + wcid->idx = MT792x_WCID_IDX_UNSET; +} + +/* Release any lazily-allocated per-peer NAN group key WTBLs for a departing +* station. Called from mt7925_mac_link_sta_remove() after the primary WTBL +* is already freed, so the NAN-specific cleanup is clearly separated from the +* generic STA teardown path. +*/ +static void mt7925_nan_peer_wcids_free (struct mt76_dev *mdev, +struct ieee80211_vif *vif, +struct ieee80211_sta *sta) +{ +struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; + +if (vif->type ==NL80211_IFTYPE_NAN_DATA && + msta->nan_rx_gtk_wcid.idx != MT792x_WCID_IDX_UNSET) +mt7925_nan_wcid_free (mdev, &msta->nan_rx_gtk_wcid); + +if (msta->nan_rx_igtk_wcid.idx != MT792x_WCID_IDX_UNSET) +mt7925_nan_wcid_free (mdev, &msta->nan_rx_igtk_wcid); +} + static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, struct ieee80211_link_sta *link_sta,
@@ -1350,6 +1394,9 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev,mt76_wcid_cleanup (mdev, wcid);mt76_wcid_mask_clear (mdev->wcid_mask, idx); +if (vif->type ==NL80211_IFTYPE_NAN || vif->type ==NL80211_IFTYPE_NAN_DATA ) +mt7925_nan_peer_wcids_free (mdev, vif, link_sta->sta); +mt76_connac_power_save_sched (&dev->mphy, &dev->pm); }
drivers/net/wireless/mediatek/mt76/mt792x.h +11 -0
@@ -18,6 +18,11 @@#define MT792x_WTBL_SIZE 20#define MT792x_WTBL_RESERVED (MT792x_WTBL_SIZE - 1)#define MT792x_WTBL_STA (MT792x_WTBL_RESERVED - MT792x_MAX_INTERFACES) +/* Sentinel for a lazily-allocated wcid that has not yet been assigned a slot. +* U16_MAX is outside every valid alloc range and matches the 0xffff value +* used in mt76 DMA ring entries to mark an unused wcid field. +*/ +#define MT792x_WCID_IDX_UNSET U16_MAX#define MT792x_CFEND_RATE_DEFAULT 0x49/* OFDM 24M */ #define MT792x_CFEND_RATE_11B 0x03/* 11B LP, 11M */
@@ -148,6 +153,12 @@ struct mt792x_sta {/* NAN peer schedule */ struct mt792x_sta_nan_sched nan_sched; + +/* NAN per-peer group key WTBLs (allocated lazily when keys are installed) +* Note: TX GTK is interface-level, see mt792x_vif->nan_tx_gtk_table +*/ +struct mt76_wcid nan_rx_gtk_wcid;/* NAN_DATA peer: RX GTK WTBL (per-peer) */ +struct mt76_wcid nan_rx_igtk_wcid;/* NAN peer: RX IGTK/BIGTK WTBL (per-peer) */ };DECLARE_EWMA (rssi,10 ,8 );
Signature · 1 line
2.43.0
PATCH 8/23 wifi: mt76: mt7925: add NDC-aware TX GTK table for NAN_DATA
In reply to Sean WangFrom: Chengwei Yu <[email protected]>
NAN TX GTK is NDC-scoped: every station in the same NAN Data Cluster shares one key, and a single NAN_DATA vif can join multiple NDCs concurrently. Add mt792x_nan.nan_tx_gtk_table[] (up to MT792X_MAX_NAN_NDC = 8 RCU on-demand entries, one per NDC) and route interface-directed NAN_DATA multicast through the matching WTBL in mt792x_tx(), falling back to the interface WTBL when no entry exists.
Teardown lives in mt7925_remove_interface(), not the shared mt792x_remove_interface(): NAN_DATA vifs are mt7925-only so cleanup in the shared path would never run.
NOTE: NDC selection from skb context is not yet implemented; the lookup always picks the first installed entry until mt7925_nan_set_key() populates the table.
--- .../net/wireless/mediatek/mt76/mt7925/main.c | 36 +++++++++++ drivers/net/wireless/mediatek/mt76/mt792x.h | 64 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt792x_core.c | 27 ++++++-- 3 files changed, 122 insertions(+), 5 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +36 -0
@@ -486,12 +486,22 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)INIT_DELAYED_WORK (&mvif->nan.mac_rand_work, mt7925_nan_mac_rand_work); +if (vif->type ==NL80211_IFTYPE_NAN_DATA ) { +/* nan_tx_gtk_table[] slots are NULL from mac80211's kzalloc of +* drv_priv; entries are allocated on demand by set_key when a +* TX GTK is installed. +*/ +spin_lock_init (&mvif->nan.nan_tx_gtk_lock); + } out:mt792x_mutex_release (dev);return ret; } +static void mt7925_nan_gtk_table_cleanup (struct mt792x_dev *dev, +struct mt792x_vif *mvif); +static void mt7925_remove_interface (struct ieee80211_hw *hw,struct ieee80211_vif *vif) {
@@ -516,6 +526,9 @@ mt7925_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mconf =mt792x_link_conf_to_mconf (&vif->bss_conf);mt792x_mac_link_bss_remove (dev, mconf, &mvif->sta.deflink); +if (vif->type ==NL80211_IFTYPE_NAN_DATA ) +mt7925_nan_gtk_table_cleanup (dev, mvif); +mt792x_mutex_release (dev); }
@@ -1331,6 +1344,29 @@ static void mt7925_nan_wcid_free(struct mt76_dev *mdev, struct mt76_wcid *wcid) wcid->idx = MT792x_WCID_IDX_UNSET; } +/* Free all TX GTK WTBL entries for a NAN_DATA vif on interface teardown. +* Caller must hold dev->mt76.mutex. +*/ +static void mt7925_nan_gtk_table_cleanup (struct mt792x_dev *dev, +struct mt792x_vif *mvif) +{ +int i; + +for (i =0 ; i <MT792X_MAX_NAN_NDC ; i++) { +struct mt792x_nan_gtk_entry *e; + + e =rcu_dereference_protected (mvif->nan.nan_tx_gtk_table[i], +lockdep_is_held (&dev->mt76.mutex)); +if (!e) +continue ; +rcu_assign_pointer (mvif->nan.nan_tx_gtk_table[i],NULL ); +rcu_assign_pointer (dev->mt76.wcid[e->wcid.idx],NULL ); +mt76_wcid_cleanup (&dev->mt76, &e->wcid); +mt76_wcid_mask_clear (dev->mt76.wcid_mask, e->wcid.idx); +kfree (e); + } +} +/* Release any lazily-allocated per-peer NAN group key WTBLs for a departing * station. Called from mt7925_mac_link_sta_remove() after the primary WTBL * is already freed, so the NAN-specific cleanup is clearly separated from the
drivers/net/wireless/mediatek/mt76/mt792x.h +63 -1
82 lines not on this page · raw message
drivers/net/wireless/mediatek/mt76/mt792x_core.c +23 -4
@@ -187,11 +187,30 @@ void mt792x_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, wcid = &mlink->wcid; } +/* NAN_DATA multicast: route through the NDC-specific TX GTK WTBL. +* Full WTBL layout is documented in mt792x.h. +* NOTE: NDC selection is not yet implemented; always falls back to +* the interface WTBL until set_key populates nan_tx_gtk_table[]. +*/ if (vif && !control->sta) { -struct mt792x_vif *mvif; - - mvif = (struct mt792x_vif *)vif->drv_priv; - wcid = &mvif->sta.deflink.wcid; +struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + +if (vif->type ==NL80211_IFTYPE_NAN_DATA ) { +struct mt792x_nan_gtk_entry *entry =NULL ; +int i; + +rcu_read_lock (); +for (i =0 ; i <MT792X_MAX_NAN_NDC ; i++) { + entry =rcu_dereference (mvif->nan.nan_tx_gtk_table[i]); +if (entry) +break ; + } +rcu_read_unlock (); + + wcid = entry ? &entry->wcid : &mvif->sta.deflink.wcid; + }else { + wcid = &mvif->sta.deflink.wcid; + } } if (vif && control->sta && ieee80211_vif_is_mld(vif) &&
Signature · 1 line
2.43.0
Felix Fietkau
maintainerRe: [PATCH 08/23] wifi: mt76: mt7925: add NDC-aware TX GTK table for NAN_DATA
Quoted 13 lines · Sean Wang
From: Chengwei Yu <[email protected]>
NAN TX GTK is NDC-scoped: every station in the same NAN Data Cluster shares one key, and a single
NAN_DATAvif can join multiple NDCs concurrently. Add mt792x_nan.nan_tx_gtk_table[] (up toMT792X_MAX_NAN_NDC= 8 RCU on-demand entries, one per NDC) and route interface-directedNAN_DATAmulticast through the matching WTBL inmt792x_tx(), falling back to the interface WTBL when no entry exists.Teardown lives in
mt7925_remove_interface(), not the sharedmt792x_remove_interface():NAN_DATAvifs are mt7925-only so cleanup in the shared path would never run.
Quoted 7 lines
NOTE: NDC selection from skb context is not yet implemented; the lookup always picks the first installed entry until
mt7925_nan_set_key()populates the table.Co-developed-by Sean Wang <[email protected]>Signed-off-by Sean Wang <[email protected]>Signed-off-by Chengwei Yu <[email protected]>
I don't see anything in the code that fills nan_tx_gtk_table. Am I missing something here?
Thanks,
- Felix
PATCH 9/23 wifi: mt76: mt7925: implement mt7925_nan_set_key for NAN security
In reply to Sean WangFrom: Chengwei Yu <[email protected]>
Wire up NAN/NAN_DATA group key installation on top of the MCU command and per-peer WTBLs from prior commits:
- NAN RX IGTK/BIGTK (keyidx 4-7, sta != NULL): lazy WTBL alloc via nan_rx_igtk_wcid; installed as NAN_KEY_TYPE_MC_MGMT_RX_KEY.
- NAN_DATA RX GTK (keyidx 1-2, sta != NULL): lazy WTBL alloc via nan_rx_gtk_wcid; installed as NAN_KEY_TYPE_MC_RX_KEY.
- NAN_DATA TX GTK: -EOPNOTSUPP; NDC ID unavailable at set_key time, keeping mac80211 in SW crypto until NDC-aware support lands.
mt7925_set_key() routes NAN/NAN_DATA vifs through mt7925_nan_set_key() and blocks NAN_DATA group keys from reaching set_link_key, preventing silent HW-offload of the unsupported TX GTK path.
--- .../net/wireless/mediatek/mt76/mt7925/main.c | 27 +++ .../net/wireless/mediatek/mt76/mt7925/nan.c | 154 ++++++++++++++++++ .../net/wireless/mediatek/mt76/mt7925/nan.h | 16 ++ .../net/wireless/mediatek/mt76/mt792x_core.c | 7 + 4 files changed, 204 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +27 -0
@@ -765,6 +765,33 @@ static int mt7925_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,struct mt792x_link_sta *mlink;int err; +/* Route NAN/NAN_DATA keys. +* +* mt7925_nan_set_key() owns every key that needs a dedicated WTBL and +* returns -EOPNOTSUPP to signal "not mine, use the normal HW path". +* Three cases legitimately reach the normal path: +* +* NAN + pairwise (NM-TK): set_link_key via NMI peer WTBL +* NAN_DATA + pairwise (ND-TK): set_link_key via NDI peer WTBL +* NAN + group (TX IGTK/BIGTK): set_link_key via NAN iface WTBL +* +* NAN_DATA TX GTK is the one group key on NAN_DATA that nan_set_key +* also declines (-EOPNOTSUPP) because NDC ID is unavailable at set_key +* time. It must NOT reach set_link_key: that function would succeed +* and silently install the key on the interface WTBL, causing mac80211 +* to mark it as HW-offloaded and skip SW crypto. Return -EOPNOTSUPP +* so mac80211 uses SW encryption until TX GTK support is complete. +*/ +if (vif->type ==NL80211_IFTYPE_NAN || vif->type ==NL80211_IFTYPE_NAN_DATA ) { + err =mt7925_nan_set_key (hw, cmd, vif, sta, key); +if (err != -EOPNOTSUPP ) +return err; +/* Block NAN_DATA group keys from reaching set_link_key. */ +if (vif->type ==NL80211_IFTYPE_NAN_DATA && + !(key->flags &IEEE80211_KEY_FLAG_PAIRWISE )) +return -EOPNOTSUPP ; + } +/* The hardware does not support per-STA RX GTK, fallback * to software mode for these. */
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +154 -0
157 lines not on this page · raw message
drivers/net/wireless/mediatek/mt76/mt7925/nan.h +16 -0
@@ -127,6 +127,16 @@ enum mt7925_nan_key_type {#define WTBL_RESERVED_ENTRY 0xFFFF#define NAN_PACKET_NUMBER_LEN 6 +/* NAN key index ranges from the Wi-Fi NAN spec. mac80211 passes keyidx +* straight from wpa_supplicant without interpretation, so the driver must +* define these boundaries itself -- no kernel-wide equivalent exists. +* Wi-Fi NAN spec 7.1.3.2 (GTKSA), 7.1.3.3 (IGTKSA), 7.1.3.4 (BIGTKSA). +*/ +#define NAN_KEY_IDX_GTK_MIN 1/* GTKSA: key ID 1-2 */ +#define NAN_KEY_IDX_GTK_MAX 2 +#define NAN_KEY_IDX_MGMT_INTEG_MIN 4/* IGTKSA (4-5) + BIGTKSA (6-7) */ +#define NAN_KEY_IDX_MGMT_INTEG_MAX 7 +/* bit indices into mt792x_dev->nan_deferred_pending, set from the atomic * MCU-event RX path and consumed by mt7925_nan_deferred_work() */
@@ -600,6 +610,12 @@ int mt7925_nan_manage_key_cmd(struct mt792x_dev *dev,const u8 *peer_addr, const struct mt7925_nan_key_info *key); +int mt7925_nan_set_key (struct ieee80211_hw *hw,enum set_key_cmd cmd, +struct ieee80211_vif *vif,struct ieee80211_sta *sta, +struct ieee80211_key_conf *key); + +int mt7925_nan_wcid_alloc (struct mt792x_dev *dev,struct mt76_wcid *wcid); +void mt7925_nan_local_sched_changed (struct mt792x_dev *dev,struct ieee80211_vif *vif);
drivers/net/wireless/mediatek/mt76/mt792x_core.c +7 -0
@@ -824,6 +824,13 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) wiphy->nan_capa.max_channel_switch_time =12 ; wiphy->nan_capa.dev_capabilities =NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED ;wiphy_ext_feature_set (wiphy,NL80211_EXT_FEATURE_SECURE_NAN ); +wiphy_ext_feature_set (wiphy,NL80211_EXT_FEATURE_BEACON_PROTECTION ); +wiphy_ext_feature_set (wiphy,NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT ); +/* Per-peer group key WTBL needed for NAN RX IGTK/BIGTK delivery: +* ieee80211_key_enable_hw_accel() drops per-STA group keys unless +* this flag is set or the vif is NAN_DATA. +*/ +ieee80211_hw_set (hw,SUPPORTS_PER_STA_GTK ); } wiphy->max_scan_ie_len =MT76_CONNAC_SCAN_IE_LEN ;
Signature · 1 line
2.43.0
PATCH 10/23 wifi: mt76: mt7925: install NAN BIP keys standalone in sta_key_tlv
In reply to Sean WangFrom: Chengwei Yu <[email protected]>
mt7925_mcu_sta_key_tlv() packs a BIP_CMAC_128 key together with the CCMP key that sta_key_conf cached from an earlier key installation on the same
link: cipher_id BIP_CMAC_128, key_len 32, the CCMP key in the first 16 bytes and the CMAC key in the second 16, indexed by the CCMP key's id. That matches STA/AP mode, where the IGTK always follows the GTK on the same WTBL.
A NAN interface has no such pairing. No CCMP key is ever installed on a NAN or NAN_DATA interface WTBL, so sta_key_conf is still zeroed when a NAN TX IGTK/BIGTK arrives: the command would carry 16 bytes of zeros as the first half of the key material and key_id 0 instead of the real 4-7 key index.
Restrict the combined-key path to non-NAN interfaces so that a NAN BIP key takes the generic path instead and is installed standalone, with its own keyidx and its own 16-byte key material.
--- drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +9 -1
@@ -1378,7 +1378,15 @@ mt7925_mcu_sta_key_tlv(struct mt76_wcid *wcid,if (cipher ==CONNAC3_CIPHER_NONE )return -EOPNOTSUPP ; -if (cipher ==CONNAC3_CIPHER_BIP_CMAC_128 ) { +/* STA/AP mode installs the IGTK together with the CCMP GTK that +* sta_key_conf cached earlier on the same link. NAN has no such +* pairing: no CCMP key is ever installed on a NAN interface WTBL, +* so sta_key_conf would contribute a zero key and a stale key_id. +* Install the NAN BIP key standalone via the generic path instead. +*/ +if (cipher ==CONNAC3_CIPHER_BIP_CMAC_128 && + vif->type !=NL80211_IFTYPE_NAN && + vif->type !=NL80211_IFTYPE_NAN_DATA ) { sec->cipher_id =CONNAC3_CIPHER_BIP_CMAC_128 ; sec->key_id = sta_key_conf->keyidx; sec->key_len =32 ;
Signature · 1 line
2.43.0
PATCH 11/23 wifi: mt76: mt7925: do not disable RX NAPI twice on unload
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
mt7925e_unregister_device() disables every RX NAPI and then calls mt792x_dma_cleanup(), whose mt76_dma_cleanup() disables them again. The second napi_disable() spins waiting for NAPI_STATE_SCHED, which the first one already set and nobody will clear, so module unload hangs in D state and the module refcount underflows to -1; only a reboot recovers.
Drop the redundant loop and leave the RX NAPI teardown to mt76_dma_cleanup(), making driver hot-reload work again.
--- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7925/pci.c +6 -3
@@ -40,7 +40,6 @@ static int mt7925e_init_reset(struct mt792x_dev *dev)static void mt7925e_unregister_device (struct mt792x_dev *dev) { -int i;struct mt76_connac_pm *pm = &dev->pm;struct ieee80211_hw *hw =mt76_hw (dev);
@@ -50,8 +49,12 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev)cancel_work_sync (&dev->reset_work);cancel_work_sync (&dev->init_work);mt76_unregister_device (&dev->mt76); -mt76_for_each_q_rx (&dev->mt76, i) -napi_disable (&dev->mt76.napi[i]); +/* Do not disable the RX NAPIs here: mt76_dma_cleanup(), reached below +* via mt792x_dma_cleanup(), disables them as part of the generic +* teardown. Disabling twice makes the second napi_disable() wait +* forever for NAPI_STATE_SCHED to clear, hanging module unload in D +* state with the module refcount left at -1. +*/ cancel_delayed_work_sync (&pm->ps_work);cancel_delayed_work_sync (&dev->mlo_pm_work);cancel_work_sync (&pm->wake_work);
Signature · 1 line
2.43.0
PATCH 12/23 wifi: mt76: mt7925: assign the interface WTBL to the NAN management TXQ
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
mac80211 does not allocate vif->txq for a NAN device, so every SDF the host sends - multicast discovery and unicast follow-up alike - is queued on the dedicated management TXQ (vif->txq_mgmt) with txq->sta == NULL.
mt7925_mac_link_bss_add() only programs the wcid of vif->txq, so the management TXQ keeps its initial wcid of 0. The mt76 TXQ scheduler then resolves dev->wcid[0], i.e. the global WTBL, and every host-originated SDF is transmitted on it. That WTBL only carries the parameters used for beacons and has no usable unicast rate, so a unicast follow-up sent on it is frequently not heard by the peer and the send is reported as failed. Multicast SDFs are unaffected because they need no acknowledgment.
Program the management TXQ with the same interface WTBL that the beacon and interface paths already use, which is fully initialised here.
Instrumenting the TX descriptor write shows every follow-up leaving on WTBL 0 before the fix and on the interface WTBL after it; a 100-frame unicast follow-up burst goes from 92-96 frames received to 100.
--- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +11 -0
@@ -447,6 +447,17 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, mtxq->wcid = idx; } +/* A NAN vif has no vif->txq; its SDFs are queued on vif->txq_mgmt, +* whose wcid was never set and defaults to 0 (WTBL 0 has no unicast +* rate), so unicast follow-ups go unacknowledged. Point txq_mgmt at +* the same interface WTBL as the beacon path so SDFs continue to use +* txq_mgmt but with a properly initialised wcid. +*/ +if (vif->txq_mgmt) { + mtxq = (struct mt76_txq *)vif->txq_mgmt->drv_priv; + mtxq->wcid = idx; + } + out:return ret; }
Signature · 1 line
2.43.0
PATCH 13/23 wifi: mt76: mt7925: do not deactivate shared NAN BSS on peer STA removal
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
mt7925_mac_sta_remove_links() sent BSS_INFO(active=0) for every vif type except AP when a peer STA was removed. NAN (nan0) and NAN_DATA (ndi0) BSSes host multiple peer STAs like an AP does: removing the NMI peer on NDP teardown deactivated the shared NAN sync BSS in FW, after which FW dropped every host-initiated NAN mgmt frame (TXM: An MMPDU for an inactive BSS_INFO) until the next NAN_START. With partial availability bitmaps this made every NDP re-establishment after a teardown fail (unicast NAF TXCNT 15/15 no-ack); full bitmaps masked the bug. It also tears down the shared data BSS while other NDPs on the same NDI live.
Exclude NAN and NAN_DATA vifs like AP.
Verified on 187/235: teardown no longer emits the BSS disable cmd, the FW drop flood is gone, and half-bitmap re-establishment after nan_ndp_terminate succeeds repeatedly (was 100% fail).
--- drivers/net/wireless/mediatek/mt76/mt7925/main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7925/main.c +10 -1
@@ -1489,7 +1489,16 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif,struct mt792x_bss_conf *mconf;struct mt792x_link_sta *mlink; -if (vif->type ==NL80211_IFTYPE_AP ) +/* AP, NAN, and NAN_DATA BSSes host multiple peer STAs; removing +* one peer must not deactivate the shared BSS. For NAN, doing +* so would kill the sync BSS and the firmware would drop all +* subsequent host-originated NAN management frames. For +* NAN_DATA, it would tear down all other NDPs sharing the same +* NDI. +*/ +if (vif->type ==NL80211_IFTYPE_AP || + vif->type ==NL80211_IFTYPE_NAN || + vif->type ==NL80211_IFTYPE_NAN_DATA )break ;if (vif->type ==NL80211_IFTYPE_STATION && sta->tdls)
Signature · 1 line
2.43.0
PATCH 14/23 wifi: mt76: gate NAN unicast mgmt by peer availability via STA queues
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
Host-initiated NAN action frames (NDP Request/Response/Confirm) were sent on MT_TXQ_PSD -> MT_LMAC_ALTX0 through the interface WTBL: a force-TX path with no relation to the NAN timeline. With a partial availability bitmap the peer is off-channel most of the time, so the 15-retry burst (~11-51ms) regularly misses the peer entirely and NDP setup fails probabilistically (half ~2/3, quarter ~0), and per spec unicast NAN mgmt must be transmitted within the peer availability windows.
Route unicast NAN mgmt through the peer STA WTBL on its AC queue
instead: the FW pauses/resumes the peer WTBL AC queues according to the peer schedule record (the committed-window intersection computed by the supplicant), which is the same gating the NDP data path already relies on. Multicast NAN mgmt (and unicast without a peer STA) keeps using the interface WTBL, which has no pause driver.
--- drivers/net/wireless/mediatek/mt76/tx.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
drivers/net/wireless/mediatek/mt76/tx.c +12 -0
@@ -681,6 +681,18 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid, head == &wcid->tx_offchannel)) qid =MT_TXQ_PSD ; +/* NAN unicast mgmt (NAFs) must stay on its AC queue: the +* PSD path maps to the ALTX (force-TX) hardware queue, +* which escapes the per-peer STA_PAUSE availability gating +* and transmits regardless of the peer's committed bitmap. +*/ +if (qid ==MT_TXQ_PSD && wcid->sta && + info->control.vif && + (info->control.vif->type ==NL80211_IFTYPE_NAN || + info->control.vif->type ==NL80211_IFTYPE_NAN_DATA ) && + head != &wcid->tx_offchannel) + qid =skb_get_queue_mapping (skb); + q = phy->q_tx[qid];if (mt76_txq_stopped (q) ||test_bit (MT76_RESET , &phy->state)) { ret =-1 ;
Signature · 1 line
2.43.0
PATCH 15/23 wifi: mt76: mt7925: hold NAN mgmt TX to the discovery window
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
Host-initiated NAN management - SDF follow-ups and the frames that open an NDP setup - rides the interface WTBL and airs the moment the host hands it over. Until its NDL is confirmed a peer is only reliably awake in the discovery window, so those frames regularly land while it is off channel. The same WTBL carries the beacons and the multicast data that must never be held, so gating that queue is not an option.
Create a dedicated STA and steer host management TX onto it, so the firmware can hold that one queue outside the DW while the interface WTBL keeps flowing. The driver hands the STA's WTBL index down with NAN_UNI_CMD_DW_WTBL_IDX right after the STA record is added - the index is the handle the firmware's queue pause works on, so no reserved address is agreed on and the record's address is an arbitrary locally administered one that never goes on air. A firmware that predates the tag ignores it, and on a failed handoff the firmware's gate stays disarmed, so management TX degrades to stock (ungated) behavior rather than breaking NAN. The split is done per frame: multicast stays on the interface WTBL, since a STA-type WTBL spends the full retry budget on an unACKable multicast RA and one frame then eats a whole DW window, and only unencrypted unicast - discovery and NDP setup - is held to the DW. Secured frames belong to an established peer and keep its own WTBL and key.
MT_TXQ_PSD reaches ALTX and escapes the pause, so the existing NAN reroute is extended to cover this WTBL. The STA record is driven to the associated state with its rate table configured; without either the firmware buffers the queue or the hardware falls back to the lowest rate, both of which show up as management frames failing after seconds. On teardown the wcid is released only after pending TX status entries are flushed, or the next status sweep dereferences them (KASAN use-after-free via wpdma_reset -> mt76_tx_status_check).
--- drivers/net/wireless/mediatek/mt76/mt76.h | 1 + .../net/wireless/mediatek/mt76/mt7925/mcu.c | 11 +- .../net/wireless/mediatek/mt76/mt7925/mcu.h | 11 ++ .../net/wireless/mediatek/mt76/mt7925/nan.c | 174 +++++++++++++++++- .../net/wireless/mediatek/mt76/mt7925/nan.h | 8 + .../wireless/mediatek/mt76/mt7925/pci_mac.c | 48 +++++ drivers/net/wireless/mediatek/mt76/mt792x.h | 7 + drivers/net/wireless/mediatek/mt76/tx.c | 2 +- 8 files changed, 250 insertions(+), 12 deletions(-)
drivers/net/wireless/mediatek/mt76/mt76.h +1 -0
@@ -397,6 +397,7 @@ struct mt76_wcid { u8 sta:1; u8 sta_disabled:1; u8 amsdu:1; + u8 nan_dw:1; u8 phy_idx:2; u8 link_id:4;bool link_valid;
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +1 -10
@@ -1972,16 +1972,7 @@ mt7925_mcu_sta_state_v2_tlv(struct mt76_phy *mphy, struct sk_buff *skb,struct ieee80211_vif *vif, u8 rcpi, u8 sta_state) { - struct sta_rec_state_v2 { - __le16 tag; -__le16 len; -u8 state; -u8 rsv[3 ]; -__le32 flags; -u8 vht_opmode; -u8 action; -u8 rsv2[2 ]; - }__packed * state; + struct sta_rec_state_v2 *state;struct tlv *tlv; tlv =mt76_connac_mcu_add_tlv (skb,STA_REC_STATE ,sizeof (*state));
drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +11 -0
@@ -466,6 +466,17 @@ struct sta_rec_hdr_trans {u8 rsv; } __packed; +struct sta_rec_state_v2 { +__le16 tag; +__le16 len; +u8 state; +u8 rsv[3 ]; +__le32 flags; +u8 vht_opmode; +u8 action; +u8 rsv2[2 ]; +} __packed; +struct sta_rec_mld {__le16 tag;__le16 len;
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +173 -1
204 lines not on this page · raw message
drivers/net/wireless/mediatek/mt76/mt7925/nan.h +8 -0
@@ -92,6 +92,7 @@ enum nan_uni_cmd_tag {NAN_UNI_CMD_SET_SYNC_RSSI =39 ,NAN_UNI_CMD_SET_CLUSTER_ID =40 ,NAN_UNI_CMD_KEY_MANAGEMENT =53 , + NAN_UNI_CMD_DW_WTBL_IDX = 55, }; enum nan_uni_event_tag {
@@ -379,6 +380,13 @@ struct mt7925_nan_nmi_addr_tlv {u8 reserved[2 ]; } __packed__aligned(4) ; +struct mt7925_nan_dw_wtbl_idx_tlv { +__le16 tag; +__le16 len; +__le16 wlan_idx; +u8 reserved[2 ]; +} __packed__aligned(4) ; +struct mt7925_nan_avail_ctrl_tlv {__le16 tag;__le16 len;
drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c +48 -0
60 lines not on this page · raw message
drivers/net/wireless/mediatek/mt76/mt792x.h +7 -0
@@ -288,6 +288,13 @@ struct mt792x_vif {struct timer_list csa_timer;struct mt792x_nan nan; + +/* NAN DW-WTBL: driver-created STA the firmware gates to the DW; +* host mgmt TX (SDF / pre-NDP NAF) is steered onto it so the BMC +* WTBL carries multicast data only and is never paused for it. +* idx == 0 or >= MT792x_WTBL_STA means not set up. +*/ +struct mt76_wcid nan_dw_wcid; };struct mt792x_phy {
drivers/net/wireless/mediatek/mt76/tx.c +1 -1
@@ -686,7 +686,7 @@ mt76_txq_schedule_pending_wcid(struct mt76_phy *phy, struct mt76_wcid *wcid,* which escapes the per-peer STA_PAUSE availability gating * and transmits regardless of the peer's committed bitmap. */ - if (qid == MT_TXQ_PSD && wcid->sta && + if (qid == MT_TXQ_PSD && (wcid->sta || wcid->nan_dw) && info->control.vif && (info->control.vif->type == NL80211_IFTYPE_NAN || info->control.vif->type == NL80211_IFTYPE_NAN_DATA) &&
Signature · 1 line
2.43.0
PATCH 16/23 wifi: mt76: mt7925: double the retry budget for NAN unicast management
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
The NAN setup handshakes are one-shot at the host: wpa_supplicant aborts PASN on the first unacknowledged authentication frame and completes the NDP state machine without retransmitting an unacknowledged key-install. Right after the data path confirm both peers transmit at each other nearly simultaneously, and with half-duplex radios one 15-retry burst regularly dies inside the peer's own TX window (measured ~50% loss of the key-install on an MT7925 pair, killing the NDP setup).
Double the TXD retry budget to the field maximum for NAN unicast management so the burst outlives the cross-fire window.
--- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/mac.c +13 -0
@@ -806,6 +806,19 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi, val =FIELD_PREP (MT_TXD3_REM_TX_COUNT ,15 ); +/* NAN setup handshakes are one-shot at the host: a single lost burst +* kills the NDP (peers transmit at each other nearly simultaneously +* after M3 and half-duplex radios miss the reply). Double the retry +* budget so the burst outlives the cross-fire window. +*/ +if (vif && vif->type ==NL80211_IFTYPE_NAN ) { +struct ieee80211_hdr *nan_hdr = (struct ieee80211_hdr *)skb->data; + +if (ieee80211_is_mgmt (nan_hdr->frame_control) && + !is_multicast_ether_addr (nan_hdr->addr1)) + val =FIELD_PREP (MT_TXD3_REM_TX_COUNT ,31 ); + } +if (key) val |=MT_TXD3_PROTECT_FRAME ;if (info->flags &IEEE80211_TX_CTL_NO_ACK )
Signature · 1 line
2.43.0
PATCH 17/23 wifi: mt76: mt7925: let TX status of DW-held NAN frames outlive the DW period
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
Frames on the NAN DW-WTBL legitimately wait up to a full discovery window period (524ms) in a paused queue before they air, but the TX status tracking times a packet out MT_TX_STATUS_SKB_TIMEOUT (250ms) after DMA completion and reports it as failed. wpa_supplicant treats the false no-ack as a delivery failure and retransmits: a NIK follow-up held to the DW is then re-queued every cycle, the peer receives one copy per DW (56 copies measured over one 30s window), and the resulting pairing-confirm storm poisons the pairing-verification state, which never triggers and times out.
Extend the TX status lifetime for frames tracked on the DW wcid so the status is reported from the actual transmission.
--- drivers/net/wireless/mediatek/mt76/tx.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
drivers/net/wireless/mediatek/mt76/tx.c +11 -2
@@ -185,11 +185,20 @@ mt76_tx_status_skb_get(struct mt76_dev *dev, struct mt76_wcid *wcid, int pktid,struct mt76_tx_cb *cb =mt76_tx_skb_cb (skb);if (pktid >=0 ) { +unsigned long timeout =MT_TX_STATUS_SKB_TIMEOUT ; +if (!(cb->flags &MT_TX_CB_DMA_DONE ))continue ; -if (time_is_after_jiffies (cb->jiffies + -MT_TX_STATUS_SKB_TIMEOUT )) +/* A frame held to the NAN DW legitimately waits up to +* a full DW period (524ms) before it airs; timing its +* status out earlier reports a false no-ack to the +* one-shot NAN handshakes (PASN, NDP key install). +*/ +if (wcid->nan_dw) + timeout =HZ ; + +if (time_is_after_jiffies (cb->jiffies + timeout))continue ; }
Signature · 1 line
2.43.0
PATCH 18/23 wifi: mt76: mt7925: defer the NAN joined-cluster event out of NAN_START
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
The firmware can join an existing cluster from inside the passive scan it runs at the start of NAN_START, so NAN_EVENT_ID_JOINED_CLUSTER lands while the start path is still executing and ieee80211_vif_nan_started() is not true yet. The handler dropped the event with a warning, leaving userspace unaware of the cluster it had joined, and service discovery then never
completed: measured 4 failures in 12 NDP bring-ups on an MT7925 pair, each one accompanied by exactly one dropped event on the joining side and none on the side that created the cluster.
NAN_EVENT_ID_STARTED_CLUSTER already defers past NAN_START through nan_deferred_work for the same reason; JOINED_CLUSTER is the other exit of the same firmware warm-up branch and needs the same treatment. Queue it on that work with its own cluster id, so a started and a joined event pending at once cannot overwrite each other.
--- .../net/wireless/mediatek/mt76/mt7925/nan.c | 20 ++++++++++++++++++- .../net/wireless/mediatek/mt76/mt7925/nan.h | 1 + drivers/net/wireless/mediatek/mt76/mt792x.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +19 -1
@@ -709,8 +709,20 @@ mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv)return ; } +/* JOINED_CLUSTER has the same race as STARTED_CLUSTER above: the +* firmware joins an existing cluster from inside its start-up passive +* scan, so the event can land while NAN_START is still running and +* nan.started is not set yet. Defer it the same way instead of +* dropping it - a lost join leaves userspace unaware of the cluster it +* is in and service discovery never completes. +*/ 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" ); +spin_lock_bh (&dev->nan_deferred_lock); +memcpy (dev->nan_joined_cluster_id, cluster_id,ETH_ALEN ); +set_bit (MT7925_NAN_DEFERRED_JOINED_CLUSTER , + &dev->nan_deferred_pending); +spin_unlock_bh (&dev->nan_deferred_lock); +ieee80211_queue_work (dev->mt76.hw, &dev->nan_deferred_work);return ; }
@@ -732,6 +744,7 @@ mt7925_nan_deferred_work(struct work_struct *work) { struct mt792x_dev *dev = container_of(work, struct mt792x_dev, nan_deferred_work); +u8 joined_cluster_id[ETH_ALEN ];struct ieee80211_vif *vif;unsigned long pending;u8 cluster_id[ETH_ALEN ];
@@ -740,6 +753,7 @@ mt7925_nan_deferred_work(struct work_struct *work) pending = dev->nan_deferred_pending; dev->nan_deferred_pending =0 ;memcpy (cluster_id, dev->nan_started_cluster_id,ETH_ALEN ); +memcpy (joined_cluster_id, dev->nan_joined_cluster_id,ETH_ALEN );spin_unlock_bh (&dev->nan_deferred_lock);if (!pending)
@@ -753,6 +767,10 @@ mt7925_nan_deferred_work(struct work_struct *work)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_JOINED_CLUSTER , &pending)) +ieee80211_nan_cluster_joined (vif, joined_cluster_id, false, +GFP_KERNEL ); +if (test_bit (MT7925_NAN_DEFERRED_SCHED_UPDATE_DONE , &pending))ieee80211_nan_sched_update_done (vif); out:
drivers/net/wireless/mediatek/mt76/mt7925/nan.h +1 -0
@@ -143,6 +143,7 @@ enum mt7925_nan_key_type {*/ enum mt7925_nan_deferred_event {MT7925_NAN_DEFERRED_STARTED_CLUSTER , +MT7925_NAN_DEFERRED_JOINED_CLUSTER ,MT7925_NAN_DEFERRED_SCHED_UPDATE_DONE , };/* NAN 4.0 Table 79. Device Capability attribute format, Supported Bands */
drivers/net/wireless/mediatek/mt76/mt792x.h +1 -0
@@ -446,6 +446,7 @@ struct mt792x_dev {spinlock_t nan_deferred_lock;unsigned long nan_deferred_pending;u8 nan_started_cluster_id[ETH_ALEN ]; +u8 nan_joined_cluster_id[ETH_ALEN ]; }; static inline struct mt792x_bss_conf *
Signature · 1 line
2.43.0
PATCH 19/23 wifi: mt76: mt7925: steer NAN handshake frames by committed-bitmap state
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
Unencrypted unicast NAN management is held to the discovery window on the DW-WTBL, because a peer whose NDL is not yet confirmed is only reliably awake there. That is the right call while nothing is known about the peer, but it stays in force for the whole session, and the DW is a narrow place to live: it opens for 16 ms every 512 ms and is shared with the discovery queue, so roughly four transmit opportunities exist inside the two seconds a Data Path Response has to complete. Under load the handshake loses that race. Routing the same frames through the peer STA WTBL was what made NDP setup reliable with a partial availability bitmap in the first place, since the firmware then paces them by the committed window instead of bursting blindly.
Neither placement is right on its own: the peer WTBL is only usable once the firmware actually holds that peer's committed bitmap. A peer station existing in mac80211 does not imply that - until the CRB download lands, the peer's availability gate has no slots to open and a frame steered there parks behind the pause until the session is torn down.
Track the committed state per peer. mt7925_nan_fill_crb_committed() returns the number of committed slots it programmed, and mt7925_nan_update_crb_tlv() latches whether that count was non-zero into nan_sched.has_commit; the allocation rollback in mt792x_nan_set_peer_schedule() and the peer record removal in mt792x_nan_set_peer_rec() clear it again. Frames to a peer holding a committed bitmap keep that peer's WTBL and are served in the slots it has promised to be awake for; frames to a peer without one, and frames with no station resolved, keep riding the DW-WTBL. Secured frames are untouched and keep their own WTBL and key.
--- .../net/wireless/mediatek/mt76/mt7925/nan.c | 30 +++++++++++---- .../wireless/mediatek/mt76/mt7925/pci_mac.c | 37 +++++++------------ drivers/net/wireless/mediatek/mt76/mt792x.h | 1 + 3 files changed, 38 insertions(+), 30 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +23 -7
@@ -1446,14 +1446,14 @@ static int mt7925_nan_peer_cap_tlv(struct sk_buff *skb,return 0 ; } -static void +static u32 mt7925_nan_fill_crb_committed (struct mt7925_nan_sched_update_crb_tlv *crb_tlv,struct ieee80211_nan_peer_sched *sched) { -u32 m, slot; +u32 m, slot, total =0 ;if (!sched) -return ; +return 0 ;for (m =0 ; m <CFG80211_NAN_MAX_PEER_MAPS && m <NAN_TIMELINE_MGMT_SIZE ; m++) {
@@ -1479,10 +1479,14 @@ mt7925_nan_fill_crb_committed(struct mt7925_nan_sched_update_crb_tlv *crb_tlv,if (!ch || !ch->chanctx_conf)continue ; + total++; +for (dw =0 ; dw <NAN_TOTAL_DW ; dw++) tl->avail_map[dw] |=cpu_to_le32 (BIT (slot)); } } + +return total; } static int mt7925_nan_update_crb_tlv(struct sk_buff *skb,
@@ -1507,9 +1511,10 @@ static int mt7925_nan_update_crb_tlv(struct sk_buff *skb, crb_tlv->is_use_ranging = false; crb_tlv->comm_ndc_ctrl.is_valid = false; -mt7925_nan_fill_crb_committed (crb_tlv, sta->nan_sched); - -return 0 ; +/* Returns the number of committed slots programmed; the caller latches +* has_commit only once the command has actually reached firmware. +*/ +return mt7925_nan_fill_crb_committed (crb_tlv, sta->nan_sched); } static int
@@ -1552,6 +1557,7 @@ int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev,struct mt792x_nan *nan;struct mt76_dev *mdev;struct sk_buff *skb; +int committed;int ret;if (!dev || !sta)
@@ -1590,7 +1596,8 @@ int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev, } } -if (mt7925_nan_update_crb_tlv (skb, sta, msta)) { + committed =mt7925_nan_update_crb_tlv (skb, sta, msta); +if (committed <0 ) { ret = -ENOMEM ; goto free_skb; }
@@ -1603,6 +1610,13 @@ int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev,if (ret && idx_allocated) goto clear_idx; +/* Latch only now: firmware holds this peer's committed bitmap, so its +* availability gate has slots to open and unencrypted NAF may be +* steered at the peer WTBL. Cleared when the CRB is torn down. +*/ +if (!ret) + msta->nan_sched.has_commit = committed >0 ; +return ret; free_skb:
@@ -1613,6 +1627,7 @@ int mt792x_nan_set_peer_schedule(struct mt792x_dev *dev, clear_idx:clear_bit (msta->nan_sched.sch_idx, &nan->conn_bitmap); msta->nan_sched.idx_assigned = false; + msta->nan_sched.has_commit = false;return ret; }
@@ -1676,6 +1691,7 @@ int mt792x_nan_set_peer_rec(struct mt76_dev *mdev,clear_bit (msta->nan_sched.sch_idx, &nan->conn_bitmap); msta->nan_sched.idx_assigned = false; + msta->nan_sched.has_commit = false;return 0 ; }
drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c +14 -23
@@ -33,16 +33,15 @@ int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,* full retry budget on an unACKable multicast RA, so one frame eats a * whole DW window and the publish SDF queue backlogs. * -* Unencrypted unicast to a peer we have no station for is first -* contact (SDF, NDP request): the DW is the only rendezvous, so it -* goes on the DW-WTBL and waits for it. -* -* Once its schedule is known - the peer station exists, which is also -* when firmware holds its committed bitmap - the frame belongs on that -* station instead. Firmware then airs it inside the peer's own -* committed slots, where the peer is awake and the medium is not the -* DW pile-up, and follows the FAW onto whatever channel the window -* actually uses rather than being pinned to the DW channel. +* Unencrypted unicast is handshake traffic (SDF follow-up, NDP +* request/response, pairing bootstrap). Until firmware holds the +* peer's committed bitmap its availability gate has nothing to open, +* so a frame steered at the peer WTBL would park behind the BY_NAN +* pause; such frames ride the DW-WTBL, which firmware unpauses every +* DW - the one rendezvous both peers must be awake for. Once the CRB +* download latches has_commit, the frame keeps the peer's own WTBL +* and firmware serves it in the committed slots, which a 2-second +* handshake deadline needs (the DW alone offers only ~4 shots). * * Secured frames belong to an established peer and keep its own WTBL * and key.
@@ -52,24 +51,16 @@ int mt7925e_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,struct mt792x_vif *mvif = (void *)vif->drv_priv;bool mcast =is_multicast_ether_addr (hdr->addr1); +struct mt792x_sta *peer = (!mcast && sta) ? + (struct mt792x_sta *)sta->drv_priv :NULL ; +if (mcast && wcid == &mvif->nan_dw_wcid) { wcid = &mvif->sta.deflink.wcid; }else if (!mcast && !key && + !(peer && peer->nan_sched.has_commit) && mvif->nan_dw_wcid.idx && mvif->nan_dw_wcid.idx < MT792x_WTBL_STA) { -struct ieee80211_sta *psta; -struct mt792x_sta *pmsta; - -rcu_read_lock (); - psta =ieee80211_find_sta (vif, hdr->addr1); - pmsta = psta ? (struct mt792x_sta *)psta->drv_priv : -NULL ; - -if (pmsta && pmsta->deflink.wcid.idx) - wcid = &pmsta->deflink.wcid; -else - wcid = &mvif->nan_dw_wcid; -rcu_read_unlock (); + wcid = &mvif->nan_dw_wcid; } }
drivers/net/wireless/mediatek/mt76/mt792x.h +1 -0
@@ -133,6 +133,7 @@ struct mt792x_sta_nan_sched {u16 committed_dw;u32 sch_idx;bool idx_assigned; +bool has_commit;/* last CRB carried a non-empty committed map */ unsigned long ndp_ctx_bitmap;bool ndp_ctx_assigned;u8 ndp_ctx_id;/* assigned NDP context ID (for NDI sta) */
Signature · 1 line
2.43.0
PATCH 20/23 wifi: mt76: mt7925: disable only the RX NAPI instances that exist
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
mt7925e_mac_reset() picks the RX NAPI instances to disable from the interrupt masks, but the instances themselves are created per allocated RX queue by mt76_dma_init(). On mt7925 the two do not agree: the tx-done ring is only described by the mt7928 DMA layout, so q_rx[MT_RXQ_MCU_WA] is never allocated and never gets a NAPI, while mt7925_irq_map still carries rx.wm2_complete_mask.
A MAC reset therefore calls napi_disable() on an instance whose net_device pointer is NULL. The fault happens inside napi_disable() while the reset work holds the RTNL, so networking goes down with it and the machine is left answering ping with no usable userspace; only a power cycle recovers it. Repeated NAN data path setup and teardown hits this reliably on a busy channel, where MAC resets are frequent.
Iterate the RX queues instead, the same way the restore path further down re-enables them, so only instances that were actually added are touched.
--- .../wireless/mediatek/mt76/mt7925/pci_mac.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7925/pci_mac.c +13 -6
@@ -127,12 +127,19 @@ int mt7925e_mac_reset(struct mt792x_dev *dev)mt76_txq_schedule_all (&dev->mphy);mt76_worker_disable (&dev->mt76.tx_worker); -if (irq_map->rx.data_complete_mask) -napi_disable (&dev->mt76.napi[MT_RXQ_MAIN ]); -if (irq_map->rx.wm_complete_mask) -napi_disable (&dev->mt76.napi[MT_RXQ_MCU ]); -if (irq_map->rx.wm2_complete_mask) -napi_disable (&dev->mt76.napi[MT_RXQ_MCU_WA ]); + +/* +* Disable the RX NAPI instances that were actually created. They are +* added per allocated RX queue by mt76_dma_init(), while the interrupt +* masks describe what the hardware is able to raise - on mt7925 the +* tx-done queue is never allocated (only mt7928 defines that ring), so +* its NAPI has no net_device and napi_disable() faults on it while +* holding the RTNL, wedging the whole machine. Iterate the queues, the +* same way the restore path below re-enables them. +*/ +mt76_for_each_q_rx (&dev->mt76, i) +napi_disable (&dev->mt76.napi[i]); +if (irq_map->tx.all_complete_mask)napi_disable (&dev->mt76.tx_napi);
Signature · 1 line
2.43.0
PATCH 21/23 wifi: mt76: mt7925: stop queueing resets once the device is being removed
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
mt7925e_unregister_device() cancels reset_work before it tears the device down, but every source that can raise a reset stays live past that point:
the MCU command path, the system error recovery and interrupt handlers, and the MAC watchdog all
call mt792x_reset(), and the interrupt tasklet is only disabled at the very end of the function. A reset raised in that window is queued behind the cancel and then runs while the device is being dismantled - mt7925_mac_reset_work() sets hw_full_reset, stops the queues and cancels the PM works before it can notice that the device is gone.
mt792x_reset() already bails out on !hw_init_done, and that flag has no other consumer: it is set once during hardware init and read only there. Clear it at the top of the teardown so no reset can be queued for its whole duration.
Measured on rauru with kprobes on mt792x_reset() (queue), on mt7925_mac_reset_work() (execution) and on mt76_unregister_device(), which runs immediately after the cancel and so serves as the anchor, while chip_reset was written in a loop across the module unload:
before: 205 resets queued and 415 mt7925_mac_reset_work() runs after the
anchor, that is after cancel_work_sync() had already returned
after: no run after the anchor; mt792x_reset() is still entered but
returns early
A chip_reset on a running device still triggers a reset as before, and unload/reload cycles stay clean.
--- drivers/net/wireless/mediatek/mt76/mt7925/pci.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
drivers/net/wireless/mediatek/mt76/mt7925/pci.c +10 -0
@@ -46,6 +46,16 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev)if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN)wiphy_rfkill_stop_polling (hw->wiphy); +/* Stop new resets from being queued for the rest of the teardown. +* mt792x_reset() bails out on !hw_init_done, which is otherwise only +* set once at init, so clearing it here closes the window in which an +* MCU timeout, a system error recovery interrupt or the watchdog +* could still schedule +* reset_work behind the cancel below and run it against a device that +* is already being dismantled. +*/ + dev->hw_init_done = false; +cancel_work_sync (&dev->reset_work);cancel_work_sync (&dev->init_work);mt76_unregister_device (&dev->mt76);
Signature · 1 line
2.43.0
PATCH 22/23 wifi: mt76: mt7925: bound the lifetime of NAN unicast management frames
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
A NAN unicast management frame sent to a peer that has disappeared never comes back to the host. The frame sits on the gated DW-WTBL queue, the gate opens at every discovery window and the hardware retransmits, but the retry budget is re-armed each time the queue is released, so it never runs out and no TX status is ever generated. The host keeps the skb in its status table indefinitely, the supplicant waits for a TX status that does not arrive, and every later management frame on that queue lines up behind the dead one. In practice a single lost peer stalls all further NAN handshakes on the interface.
Set MAX_TX_TIME in the TXD for these frames so the hardware bounds them in time rather than in attempts. When the limit expires the frame is dropped with the lifetime-expired bit set, the host sees a no-ACK status, and the queue drains. Forty-six units of 64 TU is about 3 s, six discovery windows, which is beyond the 2 s NDP handshake deadline so a frame that still has a chance to be delivered is never cut short.
--- drivers/net/wireless/mediatek/mt76/mt7925/mac.c | 12 +++++++++++- drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h | 3 +++ 2 files changed, 14 insertions(+), 1 deletion(-)
drivers/net/wireless/mediatek/mt76/mt7925/mac.c +11 -1
@@ -815,8 +815,18 @@ mt7925_mac_write_txwi(struct mt76_dev *dev, __le32 *txwi,struct ieee80211_hdr *nan_hdr = (struct ieee80211_hdr *)skb->data;if (ieee80211_is_mgmt (nan_hdr->frame_control) && - !is_multicast_ether_addr (nan_hdr->addr1)) + !is_multicast_ether_addr (nan_hdr->addr1)) { val =FIELD_PREP (MT_TXD3_REM_TX_COUNT ,31 ); + +/* The retry budget alone never finalises a frame whose +* peer has gone: the DW-WTBL gate re-arms it every DW, +* so firmware holds the frame indefinitely and the host +* never gets a TX status. Bound it in time instead - +* about six DWs, beyond the 2 s handshake deadline. +*/ + txwi[2 ] |=cpu_to_le32 (FIELD_PREP (MT_TXD2_MAX_TX_TIME , +NAN_MGMT_MAX_TX_TIME )); + } }if (key)
drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +3 -0
@@ -26,6 +26,9 @@#define MT7925_SKU_MAX_DELTA_IDX MT7925_SKU_RATE_NUM#define MT7925_SKU_TABLE_SIZE (MT7925_SKU_RATE_NUM + 1) +/* NAN unicast mgmt TXD MAX_TX_TIME, units of 64 TU: 46 is about 3 s */ +#define NAN_MGMT_MAX_TX_TIME 46 +#define MCU_UNI_EVENT_ROC 0x27#define HIF_TRAFFIC_IDLE 0x2
Signature · 1 line
2.43.0
PATCH 23/23 wifi: mt76: mt7925: always deliver the joined-cluster event through the deferred work
In reply to Sean WangFrom: Jacobs Wu <[email protected]>
A JOINED_CLUSTER arriving while nan.started is already set is sent to mac80211 directly, but a deferred STARTED_CLUSTER may still be in flight:NAN_START holds the wiphy lock, so the deferred work has often already snapshotted and cleared its pending bits and now sleeps on that lock before it can deliver STARTED. The directly sent JOINED then reaches userspace first and the late STARTED overwrites it, so the supplicant keeps the stale self cluster for the whole session and its RX filters reject the real cluster's SDFs (10 forced-split bring-ups out of ~600 showed this inversion on the bench).
Route JOINED through the deferred work unconditionally. The work always delivers STARTED before JOINED, so firmware event order is preserved no matter when the events land, and the extra scheduling hop on an idle work is negligible for this once-per-session event.
--- .../net/wireless/mediatek/mt76/mt7925/nan.c | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-)
drivers/net/wireless/mediatek/mt76/mt7925/nan.c +17 -19
@@ -709,31 +709,29 @@ mt7925_nan_mcu_handle_de_event(struct mt792x_dev *dev, struct tlv *tlv)return ; } -/* JOINED_CLUSTER has the same race as STARTED_CLUSTER above: the -* firmware joins an existing cluster from inside its start-up passive -* scan, so the event can land while NAN_START is still running and -* nan.started is not set yet. Defer it the same way instead of -* dropping it - a lost join leaves userspace unaware of the cluster it -* is in and service discovery never completes. -*/ -if (!dev->nan_vif || !ieee80211_vif_nan_started (dev->nan_vif)) { -spin_lock_bh (&dev->nan_deferred_lock); -memcpy (dev->nan_joined_cluster_id, cluster_id,ETH_ALEN ); -set_bit (MT7925_NAN_DEFERRED_JOINED_CLUSTER , - &dev->nan_deferred_pending); -spin_unlock_bh (&dev->nan_deferred_lock); -ieee80211_queue_work (dev->mt76.hw, &dev->nan_deferred_work); -return ; - } -dev_dbg (dev->mt76.dev,"nan: anchor_master_rank=%*phN\n" ,NAN_ANCHOR_MASTER_RANK_NUM , de_evt->anchor_master_rank);dev_dbg (dev->mt76.dev,"nan: own_nmi=%pM master_nmi=%pM\n" , de_evt->own_nmi, de_evt->master_nmi); -/* joined an existing cluster, not a self-anchored new one */ -ieee80211_nan_cluster_joined (dev->nan_vif, cluster_id, false,GFP_KERNEL ); +/* JOINED_CLUSTER has the same race as STARTED_CLUSTER above (the +* firmware joins from inside its start-up passive scan, so the event +* can land while NAN_START is still running), and it can also race +* the work that is about to deliver a deferred STARTED_CLUSTER - +* userspace keeps the last event it sees, so a directly sent JOINED +* would be overwritten by the stale self cluster for the whole +* session. Always deliver JOINED through the work, which sends +* STARTED before JOINED. +*/ +dev_dbg (dev->mt76.dev,"nan: deferring JOINED_CLUSTER cluster=%pM\n" , + cluster_id); +spin_lock_bh (&dev->nan_deferred_lock); +memcpy (dev->nan_joined_cluster_id, cluster_id,ETH_ALEN ); +set_bit (MT7925_NAN_DEFERRED_JOINED_CLUSTER , + &dev->nan_deferred_pending); +spin_unlock_bh (&dev->nan_deferred_lock); +ieee80211_queue_work (dev->mt76.hw, &dev->nan_deferred_work); }/* Runs the deferred NAN MCU events in process context; takes wiphy_lock
Signature · 1 line
2.43.0