diff --git a/mt7925/init.c b/mt7925/init.c index 05f9d8b1..68962501 100644 --- a/mt7925/init.c +++ b/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/mt7925/main.c b/mt7925/main.c index 49b6f16b..5ea13289 100644 --- a/mt7925/main.c +++ b/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/mt7925/mt7925.h b/mt7925/mt7925.h index 321e7323..33782d9b 100644 --- a/mt7925/mt7925.h +++ b/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/mt7925/nan.c b/mt7925/nan.c index 445b8bbd..5fa0f11d 100644 --- a/mt7925/nan.c +++ b/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/mt7925/nan.h b/mt7925/nan.h index 08c13768..f9644945 100644 --- a/mt7925/nan.h +++ b/mt7925/nan.h @@ -97,15 +97,25 @@ 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, @@ -345,7 +355,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/mt7925/pci.c b/mt7925/pci.c index 02ef09dd..24585c99 100644 --- a/mt7925/pci.c +++ b/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/mt7925/usb.c b/mt7925/usb.c index 1757023e..afd62a6d 100644 --- a/mt7925/usb.c +++ b/mt7925/usb.c @@ -348,6 +348,19 @@ failed: } #endif /* CONFIG_PM */ +static void mt7925u_disconnect(struct usb_interface *usb_intf) +{ + struct mt792x_dev *dev = usb_get_intfdata(usb_intf); + + /* mt792xu_disconnect() ends in mt76_free_device(), so the work has to + * go before it rather than after mt76_unregister_device() as on PCI. + */ + if (dev) + cancel_work_sync(&dev->nan_deferred_work); + + mt792xu_disconnect(usb_intf); +} + MODULE_DEVICE_TABLE(usb, mt7925u_device_table); MODULE_FIRMWARE(MT7925_FIRMWARE_WM); MODULE_FIRMWARE(MT7925_ROM_PATCH); @@ -356,7 +369,7 @@ static struct usb_driver mt7925u_driver = { .name = KBUILD_MODNAME, .id_table = mt7925u_device_table, .probe = mt7925u_probe, - .disconnect = mt792xu_disconnect, + .disconnect = mt7925u_disconnect, #ifdef CONFIG_PM .suspend = mt7925u_suspend, .resume = mt7925u_resume, diff --git a/mt792x.h b/mt792x.h index 9efc251c..9722d840 100644 --- a/mt792x.h +++ b/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 *