Quite a few more updates:

- cfg80211/mac80211:
    - various security(-ish) fixes
    - fix A-MSDU subframe handling
    - fix multi-link element parsing
  - ath10: avoid sending commands to dead device
  - ath11k:
    - fix WMI buffer leaks on error conditions
    - fix UAF in RX MSDU coalesce path
    - allow peer ID 0 on RX path (legal for mobile devices)
    - reinitialize shared SRNG pointers on restart
  - ath12k:
    - fix 20 MHz-only parsing of EHT-MCS map
  - iwlwifi:
    - fix TSO segmentation explosion
    - don't TX to dead device
    - fix warning in WoWLAN
    - fix TX rates on old devices
    - disconnect on beacon loss only if also no other traffic
    - fill NULL-ptr deref
    - fix STEP_URM hardware access
 -----BEGIN PGP SIGNATURE-----
 
 iQIyBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmoPJEkACgkQ10qiO8sP
 aACQrw/4vU+lbZNW19OyaJMd4h+44gUW+UGJixOzputQCBc6JGUlRsxgceWq5Ws5
 5x2LTOX7S1wcvKm0VuSvkIRP3e9YHcgB60iBtsJ3ozz4RCoCFiSu8Bb2RdkGtRTp
 7CKMK9NNuovOJncBzfyANq4ujsGs/58BmGbhXbaZ0ACfLUauesCCUtM7iQZE1k7t
 lBqtk8ezkz1L8006w5vR7VR8g4KCCofQTEAOASmx450ZeGAiHMlWVKdMFFHV3zWj
 ZDXopvLaMtduLNq9xqGYCRhAIZqOv1axgL7w9RRxsi2gWHv71kLqyz0IzgbFmh1m
 ZxUSQ45+MHVYCHxs7HHCcTR5gqQlx47j5Wi3tuLUH8yoSZ8dPeWjmQMvIEswfZql
 WNq18o6mcK+L3Yg87+oxiiJ7V/euaM//0+ZGtqhbiB+2FyHZhO42BqALTy7e4swS
 kmEl8gCj2lgCbD2AHJQ9VpOJwoNdNuLYoJqg9IiIu/CYqQF80FGO8e6HZXBXsJkL
 3KAPQIXkMMMkSjtpTg/GdHDiFqv/7lF8u3FgED7w7M1ZVQYNUc13KiDwPALFV0pu
 bBbRktB6lvF6ShW9XrTrmn9lT0iiWHxr5YctWoys4+Ofr5V7PUzxNVDxDuzSVCZ0
 apLYZ7uwSXO37q99p/azs47dzYp7tpnwKd4rpQuUTl/9bYErUA==
 =vzwG
 -----END PGP SIGNATURE-----

Merge tag 'wireless-2026-05-21' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless

Johannes Berg says:

====================
Quite a few more updates:
 - cfg80211/mac80211:
   - various security(-ish) fixes
   - fix A-MSDU subframe handling
   - fix multi-link element parsing
 - ath10: avoid sending commands to dead device
 - ath11k:
   - fix WMI buffer leaks on error conditions
   - fix UAF in RX MSDU coalesce path
   - allow peer ID 0 on RX path (legal for mobile devices)
   - reinitialize shared SRNG pointers on restart
 - ath12k:
   - fix 20 MHz-only parsing of EHT-MCS map
 - iwlwifi:
   - fix TSO segmentation explosion
   - don't TX to dead device
   - fix warning in WoWLAN
   - fix TX rates on old devices
   - disconnect on beacon loss only if also no other traffic
   - fill NULL-ptr deref
   - fix STEP_URM hardware access

* tag 'wireless-2026-05-21' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless: (24 commits)
  wifi: cfg80211: wext: validate chandef in monitor mode
  wifi: mac80211: consume only present negotiated TTLM maps
  wifi: wilc1000: fix dma_buffer leak on bus acquire failure
  wifi: mac80211: capture fast-RX rate before mesh reuses skb->cb
  wifi: mac80211: fix multi-link element inheritance
  wifi: mac80211: fix MLE defragmentation
  wifi: mac80211: don't override max_amsdu_subframes
  wifi: mac80211: bounds-check link_id in ieee80211_ml_epcs
  wifi: ath12k: fix EHT TX MCS limitation due to wrong 20 MHz-only parsing
  wifi: ath11k: clear shared SRNG pointer state on restart
  wifi: ath11k: fix use after free in ath11k_dp_rx_msdu_coalesce()
  wifi: ath11k: fix peer resolution on rx path when peer_id=0
  wifi: iwlwifi: mld: disconnect only after 6 beacons without Rx
  wifi: iwlwifi: mld: don't WARN on WoWLAN suspend w/o BSS vif
  wifi: iwlwifi: use correct function to read STEP_URM register
  wifi: iwlwifi: mvm: fix driver-set TX rates on old devices
  wifi: iwlwifi: mld: don't dereference a pointer before NULL checking it
  wifi: iwlwifi: mld: stop TX during firmware restart
  wifi: iwlwifi: mld: fix TSO segmentation explosion when AMSDU is disabled
  wifi: ath10k: skip WMI and beacon transmission when device is wedged
  ...
====================

Link: https://patch.msgid.link/20260521152903.374070-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-05-21 11:03:58 -07:00
commit 0e3c08f1b7
21 changed files with 276 additions and 124 deletions

View File

@ -3,7 +3,6 @@
* Copyright (c) 2005-2011 Atheros Communications Inc.
* Copyright (c) 2011-2017 Qualcomm Atheros, Inc.
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
@ -1947,15 +1946,15 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id)
ret = -ESHUTDOWN;
ath10k_dbg(ar, ATH10K_DBG_WMI,
"drop wmi command %d, hardware is wedged\n", cmd_id);
} else {
/* try to send pending beacons first. they take priority */
ath10k_wmi_tx_beacons_nowait(ar);
ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
ret = -ESHUTDOWN;
}
/* try to send pending beacons first. they take priority */
ath10k_wmi_tx_beacons_nowait(ar);
ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id);
if (ret && test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags))
ret = -ESHUTDOWN;
(ret != -EAGAIN);
}), 3 * HZ);

View File

@ -1761,6 +1761,7 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
int buf_first_hdr_len, buf_first_len;
struct hal_rx_desc *ldesc;
int space_extra, rem_len, buf_len;
bool is_continuation;
u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz;
/* As the msdu is spread across multiple rx buffers,
@ -1810,7 +1811,8 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
rem_len = msdu_len - buf_first_len;
while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) {
rxcb = ATH11K_SKB_RXCB(skb);
if (rxcb->is_continuation)
is_continuation = rxcb->is_continuation;
if (is_continuation)
buf_len = DP_RX_BUFFER_SIZE - hal_rx_desc_sz;
else
buf_len = rem_len;
@ -1828,7 +1830,7 @@ static int ath11k_dp_rx_msdu_coalesce(struct ath11k *ar,
dev_kfree_skb_any(skb);
rem_len -= buf_len;
if (!rxcb->is_continuation)
if (!is_continuation)
break;
}
@ -2214,8 +2216,7 @@ ath11k_dp_rx_h_find_peer(struct ath11k_base *ab, struct sk_buff *msdu)
lockdep_assert_held(&ab->base_lock);
if (rxcb->peer_id)
peer = ath11k_peer_find_by_id(ab, rxcb->peer_id);
peer = ath11k_peer_find_by_id(ab, rxcb->peer_id);
if (peer)
return peer;

View File

@ -1387,14 +1387,22 @@ EXPORT_SYMBOL(ath11k_hal_srng_deinit);
void ath11k_hal_srng_clear(struct ath11k_base *ab)
{
/* No need to memset rdp and wrp memory since each individual
* segment would get cleared in ath11k_hal_srng_src_hw_init()
* and ath11k_hal_srng_dst_hw_init().
/*
* Preserve the shared pointer buffers, but clear the previous
* firmware instance's hp/tp state before handing them back to FW.
* LMAC rings reuse this shared memory without going through the
* normal SRNG hw-init path that zeros non-LMAC ring pointers.
*/
memset(ab->hal.srng_list, 0,
sizeof(ab->hal.srng_list));
memset(ab->hal.shadow_reg_addr, 0,
sizeof(ab->hal.shadow_reg_addr));
if (ab->hal.rdp.vaddr)
memset(ab->hal.rdp.vaddr, 0,
sizeof(*ab->hal.rdp.vaddr) * HAL_SRNG_RING_ID_MAX);
if (ab->hal.wrp.vaddr)
memset(ab->hal.wrp.vaddr, 0,
sizeof(*ab->hal.wrp.vaddr) * HAL_SRNG_NUM_LMAC_RINGS);
ab->hal.avail_blk_resource = 0;
ab->hal.current_blk_index = 0;
ab->hal.num_shadow_reg_configured = 0;

View File

@ -1467,11 +1467,8 @@ ath11k_hal_rx_parse_mon_status_tlv(struct ath11k_base *ab,
case HAL_RX_MPDU_START: {
struct hal_rx_mpdu_info *mpdu_info =
(struct hal_rx_mpdu_info *)tlv_data;
u16 peer_id;
peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info);
if (peer_id)
ppdu_info->peer_id = peer_id;
ppdu_info->peer_id = ath11k_hal_rx_mpduinfo_get_peerid(ab, mpdu_info);
break;
}
case HAL_RXPCU_PPDU_END_INFO: {

View File

@ -457,6 +457,7 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[])
ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id);
if (ret) {
ath11k_warn(ar->ab, "failed to send wmi ftm command: %d\n", ret);
dev_kfree_skb(skb);
goto out;
}

View File

@ -9299,7 +9299,7 @@ int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
{
struct wmi_hw_data_filter_cmd *cmd;
struct sk_buff *skb;
int len;
int ret, len;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@ -9324,7 +9324,13 @@ int ath11k_wmi_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
"hw data filter enable %d filter_bitmap 0x%x\n",
enable, filter_bitmap);
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_HW_DATA_FILTER_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
@ -9332,6 +9338,7 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
struct wmi_wow_host_wakeup_ind *cmd;
struct sk_buff *skb;
size_t len;
int ret;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@ -9345,14 +9352,20 @@ int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow host wakeup ind\n");
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_wow_enable(struct ath11k *ar)
{
struct wmi_wow_enable_cmd *cmd;
struct sk_buff *skb;
int len;
int ret, len;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@ -9367,7 +9380,13 @@ int ath11k_wmi_wow_enable(struct ath11k *ar)
cmd->pause_iface_config = WOW_IFACE_PAUSE_ENABLED;
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow enable\n");
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
@ -9376,7 +9395,7 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
struct sk_buff *skb;
struct wmi_scan_prob_req_oui_cmd *cmd;
u32 prob_req_oui;
int len;
int ret, len;
prob_req_oui = (((u32)mac_addr[0]) << 16) |
(((u32)mac_addr[1]) << 8) | mac_addr[2];
@ -9395,7 +9414,13 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "scan prob req oui %d\n",
prob_req_oui);
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_SCAN_PROB_REQ_OUI_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
@ -9405,6 +9430,7 @@ int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
struct wmi_wow_add_del_event_cmd *cmd;
struct sk_buff *skb;
size_t len;
int ret;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@ -9422,7 +9448,13 @@ int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow add wakeup event %s enable %d vdev_id %d\n",
wow_wakeup_event(event), enable, vdev_id);
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
@ -9435,6 +9467,7 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
struct sk_buff *skb;
u8 *ptr;
size_t len;
int ret;
len = sizeof(*cmd) +
sizeof(*tlv) + /* array struct */
@ -9527,7 +9560,13 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow add pattern vdev_id %d pattern_id %d pattern_offset %d\n",
vdev_id, pattern_id, pattern_offset);
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ADD_WAKE_PATTERN_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_WOW_ADD_WAKE_PATTERN_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
@ -9535,6 +9574,7 @@ int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
struct wmi_wow_del_pattern_cmd *cmd;
struct sk_buff *skb;
size_t len;
int ret;
len = sizeof(*cmd);
skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
@ -9553,7 +9593,13 @@ int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id)
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "tlv wow del pattern vdev_id %d pattern_id %d\n",
vdev_id, pattern_id);
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_DEL_WAKE_PATTERN_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_WOW_DEL_WAKE_PATTERN_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
static struct sk_buff *
@ -9697,6 +9743,7 @@ int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
struct wmi_pno_scan_req *pno_scan)
{
struct sk_buff *skb;
int ret;
if (pno_scan->enable)
skb = ath11k_wmi_op_gen_config_pno_start(ar, vdev_id, pno_scan);
@ -9706,7 +9753,13 @@ int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id,
if (IS_ERR_OR_NULL(skb))
return -ENOMEM;
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
static void ath11k_wmi_fill_ns_offload(struct ath11k *ar,
@ -9824,6 +9877,7 @@ int ath11k_wmi_arp_ns_offload(struct ath11k *ar,
u8 *buf_ptr;
size_t len;
u8 ns_cnt, ns_ext_tuples = 0;
int ret;
offload = &arvif->arp_ns_offload;
ns_cnt = offload->ipv6_count;
@ -9862,7 +9916,13 @@ int ath11k_wmi_arp_ns_offload(struct ath11k *ar,
if (ns_ext_tuples)
ath11k_wmi_fill_ns_offload(ar, offload, &buf_ptr, enable, 1);
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SET_ARP_NS_OFFLOAD_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_SET_ARP_NS_OFFLOAD_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
@ -9870,7 +9930,7 @@ int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
{
struct wmi_gtk_rekey_offload_cmd *cmd;
struct ath11k_rekey_data *rekey_data = &arvif->rekey_data;
int len;
int ret, len;
struct sk_buff *skb;
__le64 replay_ctr;
@ -9904,14 +9964,20 @@ int ath11k_wmi_gtk_rekey_offload(struct ath11k *ar,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "offload gtk rekey vdev: %d %d\n",
arvif->vdev_id, enable);
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID offload\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
struct ath11k_vif *arvif)
{
struct wmi_gtk_rekey_offload_cmd *cmd;
int len;
int ret, len;
struct sk_buff *skb;
len = sizeof(*cmd);
@ -9928,7 +9994,13 @@ int ath11k_wmi_gtk_rekey_getinfo(struct ath11k *ar,
ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "get gtk rekey vdev_id: %d\n",
arvif->vdev_id);
return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
ret = ath11k_wmi_cmd_send(ar->wmi, skb, WMI_GTK_OFFLOAD_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_GTK_OFFLOAD_CMDID getinfo\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_val)
@ -9938,6 +10010,7 @@ int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_va
struct sk_buff *skb;
u8 *buf_ptr;
u32 len, sar_len_aligned, rsvd_len_aligned;
int ret;
sar_len_aligned = roundup(BIOS_SAR_TABLE_LEN, sizeof(u32));
rsvd_len_aligned = roundup(BIOS_SAR_RSVD1_LEN, sizeof(u32));
@ -9968,7 +10041,13 @@ int ath11k_wmi_pdev_set_bios_sar_table_param(struct ath11k *ar, const u8 *sar_va
tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);
return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID);
ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_BIOS_SAR_TABLE_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
@ -9979,6 +10058,7 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
struct sk_buff *skb;
u8 *buf_ptr;
u32 len, rsvd_len_aligned;
int ret;
rsvd_len_aligned = roundup(BIOS_SAR_RSVD2_LEN, sizeof(u32));
len = sizeof(*cmd) + TLV_HDR_SIZE + rsvd_len_aligned;
@ -9998,7 +10078,13 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar)
tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |
FIELD_PREP(WMI_TLV_LEN, rsvd_len_aligned);
return ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_PDEV_SET_BIOS_GEO_TABLE_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
int ath11k_wmi_sta_keepalive(struct ath11k *ar,
@ -10009,6 +10095,7 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
struct wmi_sta_keepalive_arp_resp *arp;
struct sk_buff *skb;
size_t len;
int ret;
len = sizeof(*cmd) + sizeof(*arp);
skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len);
@ -10040,7 +10127,13 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar,
"sta keepalive vdev %d enabled %d method %d interval %d\n",
arg->vdev_id, arg->enabled, arg->method, arg->interval);
return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID);
if (ret) {
ath11k_warn(ar->ab, "failed to send WMI_STA_KEEPALIVE_CMDID\n");
dev_kfree_skb(skb);
}
return ret;
}
bool ath11k_wmi_supports_6ghz_cc_ext(struct ath11k *ar)

View File

@ -3446,7 +3446,9 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
arg->peer_eht_mcs_count++;
fallthrough;
default:
if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
if ((vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_MESH_POINT) &&
!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
bw_20 = &eht_cap->eht_mcs_nss_supp.only_20mhz;
@ -3475,7 +3477,9 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar,
arg->punct_bitmap = ~arvif->punct_bitmap;
arg->eht_disable_mcs15 = link_conf->eht_disable_mcs15;
if (!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
if ((vif->type == NL80211_IFTYPE_AP ||
vif->type == NL80211_IFTYPE_MESH_POINT) &&
!(link_sta->he_cap.he_cap_elem.phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) {
if (bw_20->rx_tx_mcs13_max_nss)
max_nss = max(max_nss, u8_get_bits(bw_20->rx_tx_mcs13_max_nss,

View File

@ -1,11 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2024-2025 Intel Corporation
* Copyright (C) 2024-2026 Intel Corporation
*/
#ifndef __iwl_mld_constants_h__
#define __iwl_mld_constants_h__
#define IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD 4
#define IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD 6
#define IWL_MLD_MISSED_BEACONS_THRESHOLD 8
#define IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG 19
#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS 5

View File

@ -1930,12 +1930,12 @@ int iwl_mld_wowlan_suspend(struct iwl_mld *mld, struct cfg80211_wowlan *wowlan)
if (WARN_ON(!wowlan))
return 1;
IWL_DEBUG_WOWLAN(mld, "Starting the wowlan suspend flow\n");
bss_vif = iwl_mld_get_bss_vif(mld);
if (WARN_ON(!bss_vif))
if (!bss_vif)
return 1;
IWL_DEBUG_WOWLAN(mld, "Starting the wowlan suspend flow\n");
if (!bss_vif->cfg.assoc) {
int ret;
/* If we're not associated, this must be netdetect */

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2024-2025 Intel Corporation
* Copyright (C) 2024-2026 Intel Corporation
*/
#include "constants.h"
@ -504,7 +504,6 @@ void iwl_mld_remove_link(struct iwl_mld *mld,
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif);
struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf);
bool is_deflink = link == &mld_vif->deflink;
u8 fw_id = link->fw_id;
if (WARN_ON(!link || link->active))
return;
@ -512,15 +511,15 @@ void iwl_mld_remove_link(struct iwl_mld *mld,
iwl_mld_rm_link_from_fw(mld, bss_conf);
/* Continue cleanup on failure */
if (!is_deflink)
kfree_rcu(link, rcu_head);
RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL);
if (WARN_ON(fw_id >= mld->fw->ucode_capa.num_links))
if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links))
return;
RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL);
RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL);
if (!is_deflink)
kfree_rcu(link, rcu_head);
}
void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,

View File

@ -834,7 +834,7 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb,
return -EINVAL;
max_tid_amsdu_len = sta->cur->max_tid_amsdu_len[tid];
if (!max_tid_amsdu_len)
if (!max_tid_amsdu_len || max_tid_amsdu_len == 1)
return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs);
/* Sub frame header + SNAP + IP header + TCP header + MSS */
@ -846,6 +846,9 @@ static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb,
*/
num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad);
if (WARN_ON_ONCE(!num_subframes))
return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs);
if (sta->max_amsdu_subframes &&
num_subframes > sta->max_amsdu_subframes)
num_subframes = sta->max_amsdu_subframes;
@ -970,6 +973,16 @@ void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq)
struct sk_buff *skb = NULL;
u8 zero_addr[ETH_ALEN] = {};
/*
* Don't transmit during firmware restart. The firmware is dead,
* so iwl_trans_tx() would return -EIO for each frame. Avoid the
* overhead of dequeuing from mac80211 only to immediately free
* the skbs, and the potential memory pressure from rapid skb
* allocation churn during high-throughput restart scenarios.
*/
if (unlikely(mld->fw_status.in_hw_restart))
return;
/*
* No need for threads to be pending here, they can leave the first
* taker all the work.

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@ -927,13 +927,18 @@ u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm,
u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx)
{
u16 flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx);
bool is_new_rate = iwl_fw_lookup_cmd_ver(fw, BEACON_TEMPLATE_CMD, 0) > 10;
u16 flags = 0;
if (rate_idx <= IWL_LAST_CCK_RATE)
flags |= is_new_rate ? IWL_MAC_BEACON_CCK
: IWL_MAC_BEACON_CCK_V1;
if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8)
flags |= iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx);
else
flags |= iwl_fw_rate_idx_to_plcp(rate_idx);
return flags;
}
@ -962,6 +967,7 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm,
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct ieee80211_tx_info *info;
u32 rate_n_flags = 0;
u8 rate;
u32 tx_flags;
@ -981,18 +987,21 @@ static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm,
IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) {
iwl_mvm_toggle_tx_ant(mvm, &mvm->mgmt_last_antenna_idx);
tx_params->rate_n_flags =
cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
RATE_MCS_ANT_POS);
rate_n_flags |= BIT(mvm->mgmt_last_antenna_idx) <<
RATE_MCS_ANT_POS;
}
rate = iwl_mvm_mac_ctxt_get_beacon_rate(mvm, info, vif);
tx_params->rate_n_flags |=
cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate));
if (rate == IWL_FIRST_CCK_RATE)
tx_params->rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK_V1);
if (rate < IWL_FIRST_OFDM_RATE)
rate_n_flags |= RATE_MCS_MOD_TYPE_CCK;
else
rate_n_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM;
rate_n_flags |= iwl_mvm_mac80211_idx_to_hwrate(mvm->fw, rate);
tx_params->rate_n_flags = iwl_mvm_v3_rate_to_fw(rate_n_flags,
mvm->fw_rates_ver);
}
int iwl_mvm_mac_ctxt_send_beacon_cmd(struct iwl_mvm *mvm,

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
* Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@ -159,15 +159,9 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags,
u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx)
{
if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8)
/* In the new rate legacy rates are indexed:
* 0 - 3 for CCK and 0 - 7 for OFDM.
*/
return (rate_idx >= IWL_FIRST_OFDM_RATE ?
rate_idx - IWL_FIRST_OFDM_RATE :
rate_idx);
return iwl_fw_rate_idx_to_plcp(rate_idx);
return rate_idx >= IWL_FIRST_OFDM_RATE ?
rate_idx - IWL_FIRST_OFDM_RATE :
rate_idx;
}
u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac)

View File

@ -398,9 +398,9 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans)
mutex_unlock(&trans_pcie->mutex);
if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
trans->step_urm = !!(iwl_read_umac_prph(trans,
CNVI_PMU_STEP_FLOW) &
CNVI_PMU_STEP_FLOW_FORCE_URM);
trans->step_urm = !!(iwl_read_prph(trans,
CNVI_PMU_STEP_FLOW) &
CNVI_PMU_STEP_FLOW_FORCE_URM);
}
static bool iwl_pcie_set_ltr(struct iwl_trans *trans)

View File

@ -1265,7 +1265,7 @@ int wilc_wlan_firmware_download(struct wilc *wilc, const u8 *buffer,
ret = acquire_bus(wilc, WILC_BUS_ACQUIRE_AND_WAKEUP);
if (ret)
return ret;
goto fail;
wilc->hif_func->hif_read_reg(wilc, WILC_GLB_RESET_0, &reg);
reg &= ~BIT(10);

View File

@ -2344,8 +2344,9 @@ static int sta_apply_parameters(struct ieee80211_local *local,
sta->sta.max_sp = params->max_sp;
}
ieee80211_sta_set_max_amsdu_subframes(sta, params->ext_capab,
params->ext_capab_len);
if (params->ext_capab)
ieee80211_sta_set_max_amsdu_subframes(sta, params->ext_capab,
params->ext_capab_len);
/*
* cfg80211 validates this (1-2007) and allows setting the AID

View File

@ -8164,6 +8164,7 @@ ieee80211_parse_neg_ttlm(struct ieee80211_sub_if_data *sdata,
"No active links for TID %d", tid);
return -EINVAL;
}
pos += map_size;
} else {
map = 0;
}
@ -8182,7 +8183,6 @@ ieee80211_parse_neg_ttlm(struct ieee80211_sub_if_data *sdata,
default:
return -EINVAL;
}
pos += map_size;
}
return 0;
}
@ -11232,6 +11232,9 @@ static void ieee80211_ml_epcs(struct ieee80211_sub_if_data *sdata,
control = get_unaligned_le16(pos);
link_id = control & IEEE80211_MLE_STA_EPCS_CONTROL_LINK_ID;
if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
continue;
link = sdata_dereference(sdata->link[link_id], sdata);
if (!link)
continue;

View File

@ -34,6 +34,22 @@
#include "led.h"
#include "wep.h"
static const u8 empty_non_inheritance[] = {
WLAN_EID_EXTENSION, 1, WLAN_EID_EXT_NON_INHERITANCE,
/*
* cfg80211_is_element_inherited() hardcodes elements that
* cannot be inherited, so we just need an empty one to be
* calling it at all.
*/
};
struct ieee80211_elem_defrag {
const struct element *elem;
/* container start/len */
const u8 *start;
size_t len;
};
struct ieee80211_elems_parse {
/* must be first for kfree to work */
struct ieee802_11_elems elems;
@ -41,11 +57,7 @@ struct ieee80211_elems_parse {
/* The basic Multi-Link element in the original elements */
const struct element *ml_basic_elem;
/* The reconfiguration Multi-Link element in the original elements */
const struct element *ml_reconf_elem;
/* The EPCS Multi-Link element in the original elements */
const struct element *ml_epcs_elem;
struct ieee80211_elem_defrag ml_reconf, ml_epcs;
bool multi_link_inner;
bool skip_vendor;
@ -162,10 +174,14 @@ ieee80211_parse_extension_element(u32 *crc,
}
break;
case IEEE80211_ML_CONTROL_TYPE_RECONF:
elems_parse->ml_reconf_elem = elem;
elems_parse->ml_reconf.elem = elem;
elems_parse->ml_reconf.start = params->start;
elems_parse->ml_reconf.len = params->len;
break;
case IEEE80211_ML_CONTROL_TYPE_PRIO_ACCESS:
elems_parse->ml_epcs_elem = elem;
elems_parse->ml_epcs.elem = elem;
elems_parse->ml_epcs.start = params->start;
elems_parse->ml_epcs.len = params->len;
break;
default:
break;
@ -916,7 +932,7 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse,
{
struct ieee802_11_elems *elems = &elems_parse->elems;
struct ieee80211_mle_per_sta_profile *prof;
const struct element *tmp;
const struct element *tmp, *ret;
ssize_t ml_len;
const u8 *end;
@ -986,50 +1002,40 @@ ieee80211_prep_mle_link_parse(struct ieee80211_elems_parse *elems_parse,
sub->from_ap = params->from_ap;
sub->link_id = -1;
return cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
sub->start, sub->len);
ret = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
sub->start, sub->len);
if (ret)
return ret;
/*
* Since we know we want and found a profile, apply an empty
* non-inheritance if the profile didn't have one, so that any
* element that shouldn't be inherited by spec isn't.
*/
return (const void *)empty_non_inheritance;
}
static void
ieee80211_mle_defrag_reconf(struct ieee80211_elems_parse *elems_parse)
static const void *
ieee80211_mle_defrag(struct ieee80211_elems_parse *elems_parse,
struct ieee80211_elem_defrag *defrag,
size_t *out_len)
{
struct ieee802_11_elems *elems = &elems_parse->elems;
const void *ret;
ssize_t ml_len;
ml_len = cfg80211_defragment_element(elems_parse->ml_reconf_elem,
elems->ie_start,
elems->total_len,
ml_len = cfg80211_defragment_element(defrag->elem,
defrag->start, defrag->len,
elems_parse->scratch_pos,
elems_parse->scratch +
elems_parse->scratch_len -
elems_parse->scratch_pos,
WLAN_EID_FRAGMENT);
if (ml_len < 0)
return;
elems->ml_reconf = (void *)elems_parse->scratch_pos;
elems->ml_reconf_len = ml_len;
elems_parse->scratch_pos += ml_len;
}
static void
ieee80211_mle_defrag_epcs(struct ieee80211_elems_parse *elems_parse)
{
struct ieee802_11_elems *elems = &elems_parse->elems;
ssize_t ml_len;
ml_len = cfg80211_defragment_element(elems_parse->ml_epcs_elem,
elems->ie_start,
elems->total_len,
elems_parse->scratch_pos,
elems_parse->scratch +
elems_parse->scratch_len -
elems_parse->scratch_pos,
WLAN_EID_FRAGMENT);
if (ml_len < 0)
return;
elems->ml_epcs = (void *)elems_parse->scratch_pos;
elems->ml_epcs_len = ml_len;
return NULL;
ret = elems_parse->scratch_pos;
*out_len = ml_len;
elems_parse->scratch_pos += ml_len;
return ret;
}
struct ieee802_11_elems *
@ -1042,6 +1048,7 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
size_t scratch_len = 3 * params->len;
bool multi_link_inner = false;
BUILD_BUG_ON(sizeof(empty_non_inheritance) != empty_non_inheritance[1] + 2);
BUILD_BUG_ON(offsetof(typeof(*elems_parse), elems) != 0);
/* cannot parse for both a specific link and non-transmitted BSS */
@ -1089,6 +1096,17 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
non_inherit = cfg80211_find_ext_elem(WLAN_EID_EXT_NON_INHERITANCE,
sub.start, nontx_len);
/*
* If it's a non-transmitted BSS, we shouldn't pick
* any elements in the outer parsing that shouldn't
* be inherited. If the profile has a non-inheritance
* element this automatically happens, but if not then
* provide an empty one so that the hard-coded elements
* in cfg80211_is_element_inherited() are ignored, but
* it must be called.
*/
if (params->bss->transmitted_bss && !non_inherit)
non_inherit = (const void *)empty_non_inheritance;
} else {
/* must always parse to get elems_parse->ml_basic_elem */
non_inherit = ieee80211_prep_mle_link_parse(elems_parse, params,
@ -1109,9 +1127,12 @@ ieee802_11_parse_elems_full(struct ieee80211_elems_parse_params *params)
_ieee802_11_parse_elems_full(&sub, elems_parse, NULL);
}
ieee80211_mle_defrag_reconf(elems_parse);
ieee80211_mle_defrag_epcs(elems_parse);
elems->ml_reconf = ieee80211_mle_defrag(elems_parse,
&elems_parse->ml_reconf,
&elems->ml_reconf_len);
elems->ml_epcs = ieee80211_mle_defrag(elems_parse,
&elems_parse->ml_epcs,
&elems->ml_epcs_len);
if (elems->tim && !elems->parse_error) {
const struct ieee80211_tim_ie *tim_ie = elems->tim;

View File

@ -4984,6 +4984,7 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
u8 sa[ETH_ALEN];
} addrs __aligned(2);
struct ieee80211_sta_rx_stats *stats;
u32 encoded_rate;
/* for parallel-rx, we need to have DUP_VALIDATED, otherwise we write
* to a common data structure; drivers can implement that per queue
@ -5091,11 +5092,14 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
/* push the addresses in front */
memcpy(skb_push(skb, sizeof(addrs)), &addrs, sizeof(addrs));
/* capture before mesh forward may memset or free skb->cb */
encoded_rate = sta_stats_encode_rate(status);
res = ieee80211_rx_mesh_data(rx->sdata, rx->sta, rx->skb);
switch (res) {
case RX_QUEUED:
stats->last_rx = jiffies;
stats->last_rate = sta_stats_encode_rate(status);
stats->last_rate = encoded_rate;
return true;
case RX_CONTINUE:
break;

View File

@ -2462,6 +2462,9 @@ size_t cfg80211_merge_profile(const u8 *ie, size_t ielen,
memcpy(merged_ie + copied_len, next_sub->data,
next_sub->datalen);
copied_len += next_sub->datalen;
mbssid_elem = next_mbssid;
sub_elem = next_sub;
}
return copied_len;

View File

@ -789,6 +789,8 @@ static int cfg80211_wext_siwfreq(struct net_device *dev,
chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq);
if (!chandef.chan)
return -EINVAL;
if (!cfg80211_chandef_valid(&chandef))
return -EINVAL;
return cfg80211_set_monitor_channel(rdev, dev, &chandef);
case NL80211_IFTYPE_MESH_POINT:
freq = cfg80211_wext_freq(wextfreq);