From: Jacobs Wu mt7925_mcu_build_scan_ie_tlv() sends the per-band sets of struct ieee80211_scan_ies but not scan_ies->common_ies. mac80211 puts only the band capabilities, and the custom IEs that must precede them, into the per-band sets; every other custom IE of the request, the vendor specific elements (P2P, WPS, MBO, ...) included, goes to common_ies. Since commit 9f8f4a51f3c1 ("wifi: mt76: mt7925: fix incorrect scan probe IE handling for hw_scan") these IEs are missing from the probe requests. Send common_ies in a UNI_SCAN_IE TLV without a band: the firmware appends such a TLV to the probe requests of every band. It keeps up to MT76_CONNAC_SCAN_IE_LEN bytes of it, which max_scan_ie_len already guarantees for hw_scan; sched_scan shares the TLV, so give it the same limit instead of the IEEE80211_MAX_DATA_LEN of the connac devices that have a sched_scan command of their own. The per-band TLVs keep carrying the per-band sets; the firmware stores their length in a byte, so a longer set is left out instead of being truncated. Size the request for the IEs it carries. hw_scan reserved a fixed sizeof(struct scan_ie_tlv) + MT76_CONNAC_SCAN_IE_LEN, which the per-band TLVs of a request can exceed, and sched_scan reserved nothing beyond the TLV header since struct scan_ie_tlv lost its ies[] array, so mt76_connac_mcu_add_tlv() put the TLVs past the end of the buffer, which skb_put() turns into a panic, whenever the kmalloc slack did not happen to cover them. The builder now checks the tailroom of the skb instead of a budget of its own. Fixes: 9f8f4a51f3c1 ("wifi: mt76: mt7925: fix incorrect scan probe IE handling for hw_scan") Cc: stable@vger.kernel.org Signed-off-by: Jacobs Wu --- .../net/wireless/mediatek/mt76/mt7925/init.c | 5 + .../net/wireless/mediatek/mt76/mt7925/mcu.c | 107 ++++++++++++------ 2 files changed, 79 insertions(+), 33 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index 8c3049b66a85..05305f4e1db8 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -301,6 +301,11 @@ int mt7925_register_device(struct mt792x_dev *dev) if (ret) return ret; + /* hw_scan and sched_scan share the UNI_SCAN_IE TLV, and the firmware + * keeps at most MT76_CONNAC_SCAN_IE_LEN bytes of IEs for either. + */ + hw->wiphy->max_sched_scan_ie_len = MT76_CONNAC_SCAN_IE_LEN; + hw->wiphy->reg_notifier = mt7925_regd_notifier; dev->mphy.sband_2g.sband.ht_cap.cap |= IEEE80211_HT_CAP_LDPC_CODING | diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 24de2ff3a15d..22a6338acaf1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -3347,56 +3347,99 @@ int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable) } EXPORT_SYMBOL_GPL(mt7925_mcu_set_dbdc); +/* Room for a UNI_SCAN_IE TLV carrying ies_len bytes of IEs */ +static u32 +mt7925_mcu_scan_ie_tlv_len(struct mt76_dev *mdev, u32 ies_len) +{ + u32 len = sizeof(struct scan_ie_tlv) + ies_len; + + if (is_mt7928(mdev)) + len = ALIGN(len, 4); + + return len; +} + +/* Room for the UNI_SCAN_IE TLVs of a scan request */ +static u32 +mt7925_mcu_scan_ies_len(struct mt76_dev *mdev, + struct ieee80211_scan_ies *scan_ies) +{ + enum nl80211_band i; + u32 len = 0; + + if (scan_ies->common_ie_len) + len += mt7925_mcu_scan_ie_tlv_len(mdev, scan_ies->common_ie_len); + + for (i = 0; i <= NL80211_BAND_6GHZ; i++) { + if (i == NL80211_BAND_60GHZ || !scan_ies->len[i]) + continue; + + len += mt7925_mcu_scan_ie_tlv_len(mdev, scan_ies->len[i]); + } + + return len; +} + +static void +mt7925_mcu_add_scan_ie_tlv(struct mt76_dev *mdev, struct sk_buff *skb, + u8 band, const u8 *ies, u32 ies_len) +{ + u32 alloc_len = mt7925_mcu_scan_ie_tlv_len(mdev, ies_len); + struct scan_ie_tlv *ie; + struct tlv *tlv; + + if (alloc_len > skb_tailroom(skb)) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE, alloc_len); + ie = (struct scan_ie_tlv *)tlv; + + memcpy(ie->ies, ies, ies_len); + ie->ies_len = cpu_to_le16(ies_len); + ie->band = band; +} + static void mt7925_mcu_build_scan_ie_tlv(struct mt76_dev *mdev, struct sk_buff *skb, struct ieee80211_scan_ies *scan_ies) { - u32 max_len = sizeof(struct scan_ie_tlv) + MT76_CONNAC_SCAN_IE_LEN; - u32 ies_len, alloc_len; - struct scan_ie_tlv *ie; enum nl80211_band i; - struct tlv *tlv; - const u8 *ies; + + /* The firmware appends a TLV without a band to the probe requests + * of every band: that is where the common IEs of the request go. + */ + if (scan_ies->common_ie_len) + mt7925_mcu_add_scan_ie_tlv(mdev, skb, 0, scan_ies->common_ies, + scan_ies->common_ie_len); for (i = 0; i <= NL80211_BAND_6GHZ; i++) { + u8 band; + if (i == NL80211_BAND_60GHZ) continue; - ies = scan_ies->ies[i]; - ies_len = scan_ies->len[i]; - - if (!ies || !ies_len) + if (!scan_ies->ies[i] || !scan_ies->len[i]) continue; - if (is_mt7928(mdev)) - alloc_len = ALIGN(sizeof(*ie) + ies_len, 4); - else - alloc_len = sizeof(*ie) + ies_len; - - if (alloc_len > max_len) - return; - - tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_IE, - alloc_len); - ie = (struct scan_ie_tlv *)tlv; - - memcpy(ie->ies, ies, ies_len); - ie->ies_len = cpu_to_le16(ies_len); + /* The firmware keeps the length of a per-band set in a byte */ + if (scan_ies->len[i] > U8_MAX) + continue; switch (i) { case NL80211_BAND_2GHZ: - ie->band = 1; + band = 1; break; case NL80211_BAND_6GHZ: - ie->band = 3; + band = 3; break; default: - ie->band = 2; + band = 2; break; } - max_len -= alloc_len; + mt7925_mcu_add_scan_ie_tlv(mdev, skb, band, scan_ies->ies[i], + scan_ies->len[i]); } } @@ -3415,7 +3458,6 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, struct scan_ssid_tlv *ssid; struct scan_bssid_tlv *bssid; struct scan_chan_info_tlv *chan_info; - struct scan_ie_tlv *ie; struct scan_misc_tlv *misc; struct tlv *tlv; int max_len; @@ -3425,8 +3467,8 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) + sizeof(*bssid) * MT7925_RNR_SCAN_MAX_BSSIDS + - sizeof(*chan_info) + sizeof(*misc) + sizeof(*ie) + - MT76_CONNAC_SCAN_IE_LEN; + sizeof(*chan_info) + sizeof(*misc) + + mt7925_mcu_scan_ies_len(mdev, &scan_req->ies); skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); if (!skb) @@ -3546,15 +3588,14 @@ int mt7925_mcu_sched_scan_req(struct mt76_phy *phy, struct scan_sched_req *req; struct scan_ssid_tlv *ssid; struct scan_chan_info_tlv *chan_info; - struct scan_ie_tlv *ie; struct scan_sched_ssid_match_sets *match; struct sk_buff *skb; struct tlv *tlv; int i, max_len; max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) + - sizeof(*chan_info) + sizeof(*ie) + - sizeof(*match); + sizeof(*chan_info) + sizeof(*match) + + mt7925_mcu_scan_ies_len(mdev, ies); skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); if (!skb) -- 2.45.2