From 0cd46d1f36ced0e61bd6d8cdce7c9eec9563d294 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 9 Apr 2025 10:26:37 +0800 Subject: [PATCH 01/16] wifi: ath12k: identify assoc link vif in station mode In MLO scenario, for station mode interface, WCN7850 firmware requests the assoc link vdev to get started before any other link vdevs starts. Firmware internally checks assoc_link field of wmi_vdev_start_mlo_params.flags when the first vdev starts. And if the check fails firmware crashes. Current connection flow guarantees the assoc link vdev gets started first (at assoc stage only one link vdev is created/started, i.e. the assoc link vdev), however the assoc_link flag is never set, resulting in WCN7850 firmware crash. Note ath12k_link_sta structure already has is_assoc_link flag, and it is properly set for assoc link. However we can not use it because it won't be available before peer gets created, which is too late for vdev starts. So add a new flag 'is_sta_assoc_link' in ath12k_link_vif structure and set it when deflink vdev is created. This is valid because we always use deflink as the assoc link. This flag is passed to firmware vdev starts to avoid firmware crash. Also verify the link vif/sta pair has the same settings when creating link sta. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00284-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00209-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang Link: https://patch.msgid.link/20250409-ath12k-wcn7850-mlo-support-v2-4-3801132ca2c3@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 3 +++ drivers/net/wireless/ath/ath12k/mac.c | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 06dddf66a83b..1bc2a391171d 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -314,6 +314,9 @@ struct ath12k_link_vif { spinlock_t link_stats_lock; /* Protects updates to link_stats */ u8 current_cntdown_counter; + + /* only used in station mode */ + bool is_sta_assoc_link; }; struct ath12k_vif { diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 7af12733a1ff..37e76fff57a5 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3477,6 +3477,8 @@ static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah, */ if (!ahvif->links_map && link_id != ATH12K_DEFAULT_SCAN_LINK) { arvif = &ahvif->deflink; + if (vif->type == NL80211_IFTYPE_STATION) + arvif->is_sta_assoc_link = true; } else { arvif = (struct ath12k_link_vif *) kzalloc(sizeof(struct ath12k_link_vif), GFP_KERNEL); @@ -5867,6 +5869,17 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, * link sta */ if (sta->mlo) { + /* For station mode, arvif->is_sta_assoc_link has been set when + * vdev starts. Make sure the arvif/arsta pair have same setting + */ + if (vif->type == NL80211_IFTYPE_STATION && + !arsta->arvif->is_sta_assoc_link) { + ath12k_hw_warn(ah, "failed to verify assoc link setting with link id %u\n", + link_id); + ret = -EINVAL; + goto exit; + } + arsta->is_assoc_link = true; ahsta->assoc_link_id = link_id; } @@ -9123,6 +9136,9 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, * link vdevs which are advertised as partners below */ ml_arg->link_add = true; + + ml_arg->assoc_link = arvif->is_sta_assoc_link; + partner_info = ml_arg->partner_info; links = ahvif->links_map; -- 2.51.0 From ead6d41116b81098061c878d9bfc0b1a6c629090 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 9 Apr 2025 10:26:38 +0800 Subject: [PATCH 02/16] wifi: ath12k: make assoc link associate first In MLO scenario WCN7850 firmware requests the assoc link to associate before any other links. However currently in ath12k_mac_op_vif_cfg_changed() we are doing association in an ascending order of link id. If the assoc link does not get assigned the smallest id, a non-assoc link gets associated first and firmware crashes. Change to do association for the assoc link first. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00284-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00209-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang Link: https://patch.msgid.link/20250409-ath12k-wcn7850-mlo-support-v2-5-3801132ca2c3@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 37e76fff57a5..48d60f7b891a 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -3614,6 +3614,8 @@ static void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw, unsigned long links = ahvif->links_map; struct ieee80211_bss_conf *info; struct ath12k_link_vif *arvif; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; struct ath12k *ar; u8 link_id; @@ -3626,6 +3628,35 @@ static void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_ASSOC) { + if (vif->cfg.assoc) { + /* only in station mode we can get here, so it's safe + * to use ap_addr + */ + rcu_read_lock(); + sta = ieee80211_find_sta(vif, vif->cfg.ap_addr); + if (!sta) { + rcu_read_unlock(); + WARN_ONCE(1, "failed to find sta with addr %pM\n", + vif->cfg.ap_addr); + return; + } + + ahsta = ath12k_sta_to_ahsta(sta); + arvif = wiphy_dereference(hw->wiphy, + ahvif->link[ahsta->assoc_link_id]); + rcu_read_unlock(); + + ar = arvif->ar; + /* there is no reason for which an assoc link's + * bss info does not exist + */ + info = ath12k_mac_get_link_bss_conf(arvif); + ath12k_bss_assoc(ar, arvif, info); + + /* exclude assoc link as it is done above */ + links &= ~BIT(ahsta->assoc_link_id); + } + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); if (!arvif || !arvif->ar) -- 2.51.0 From 3aba3a1422656850dd1f87843ca34acd379a29ce Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 9 Apr 2025 10:26:39 +0800 Subject: [PATCH 03/16] wifi: ath12k: group REO queue buffer parameters together Currently vaddr, paddr and size fields are located together with other fields in ath12k_dp_rx_tid structure. Logically they represents the REO queue buffer so better to group them in an individual structure. Introduce a new structure ath12k_reoq_buf to group them. This improves code readability, and benefits the upcoming patch where this structure is heavily accessed. While at it, change vaddr type to 'void *' since it is actually not pointing to any u32 buffer. Also rename paddr as paddr_aligned to better reflect its actual meaning. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0-03427-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.15378.4 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00284-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00209-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang Link: https://patch.msgid.link/20250409-ath12k-wcn7850-mlo-support-v2-6-3801132ca2c3@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_rx.c | 94 +++++++++++++------------ drivers/net/wireless/ath/ath12k/dp_rx.h | 10 ++- 2 files changed, 56 insertions(+), 48 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 925f063bc8fd..acafaabc4c9d 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -550,9 +550,9 @@ void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab) spin_lock_bh(&dp->reo_cmd_lock); list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { list_del(&cmd->list); - dma_unmap_single(ab->dev, cmd->data.paddr, - cmd->data.size, DMA_BIDIRECTIONAL); - kfree(cmd->data.vaddr); + dma_unmap_single(ab->dev, cmd->data.qbuf.paddr_aligned, + cmd->data.qbuf.size, DMA_BIDIRECTIONAL); + kfree(cmd->data.qbuf.vaddr); kfree(cmd); } @@ -560,9 +560,9 @@ void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab) &dp->reo_cmd_cache_flush_list, list) { list_del(&cmd_cache->list); dp->reo_cmd_cache_flush_count--; - dma_unmap_single(ab->dev, cmd_cache->data.paddr, - cmd_cache->data.size, DMA_BIDIRECTIONAL); - kfree(cmd_cache->data.vaddr); + dma_unmap_single(ab->dev, cmd_cache->data.qbuf.paddr_aligned, + cmd_cache->data.qbuf.size, DMA_BIDIRECTIONAL); + kfree(cmd_cache->data.qbuf.vaddr); kfree(cmd_cache); } spin_unlock_bh(&dp->reo_cmd_lock); @@ -577,10 +577,10 @@ static void ath12k_dp_reo_cmd_free(struct ath12k_dp *dp, void *ctx, ath12k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", rx_tid->tid, status); - dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, + dma_unmap_single(dp->ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } static int ath12k_dp_reo_cmd_send(struct ath12k_base *ab, struct ath12k_dp_rx_tid *rx_tid, @@ -635,13 +635,13 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab, unsigned long tot_desc_sz, desc_sz; int ret; - tot_desc_sz = rx_tid->size; + tot_desc_sz = rx_tid->qbuf.size; desc_sz = ath12k_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID); while (tot_desc_sz > desc_sz) { tot_desc_sz -= desc_sz; - cmd.addr_lo = lower_32_bits(rx_tid->paddr + tot_desc_sz); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned + tot_desc_sz); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); ret = ath12k_dp_reo_cmd_send(ab, rx_tid, HAL_REO_CMD_FLUSH_CACHE, &cmd, NULL); @@ -652,8 +652,8 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab, } memset(&cmd, 0, sizeof(cmd)); - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; ret = ath12k_dp_reo_cmd_send(ab, rx_tid, HAL_REO_CMD_FLUSH_CACHE, @@ -661,10 +661,10 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab, if (ret) { ath12k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", rx_tid->tid, ret); - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + dma_unmap_single(ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } } @@ -723,10 +723,10 @@ static void ath12k_dp_rx_tid_del_func(struct ath12k_dp *dp, void *ctx, return; free_desc: - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + dma_unmap_single(ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } static void ath12k_peer_rx_tid_qref_setup(struct ath12k_base *ab, u16 peer_id, u16 tid, @@ -796,8 +796,8 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar, return; cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); cmd.upd0 = HAL_REO_CMD_UPD0_VLD; ret = ath12k_dp_reo_cmd_send(ar->ab, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, @@ -805,10 +805,10 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar, if (ret) { ath12k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", tid, ret); - dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_unmap_single(ar->ab->dev, rx_tid->qbuf.paddr_aligned, + rx_tid->qbuf.size, DMA_BIDIRECTIONAL); + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } if (peer->mlo) @@ -904,8 +904,8 @@ static int ath12k_peer_rx_tid_reo_update(struct ath12k *ar, struct ath12k_hal_reo_cmd cmd = {0}; int ret; - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE; cmd.ba_window_size = ba_win_sz; @@ -940,7 +940,7 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ struct ath12k_dp_rx_tid *rx_tid; u32 hw_desc_sz; void *vaddr; - dma_addr_t paddr; + dma_addr_t paddr_aligned; int ret; spin_lock_bh(&ab->base_lock); @@ -974,7 +974,7 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ rx_tid = &peer->rx_tid[tid]; /* Update the tid queue if it is already setup */ if (rx_tid->active) { - paddr = rx_tid->paddr; + paddr_aligned = rx_tid->qbuf.paddr_aligned; ret = ath12k_peer_rx_tid_reo_update(ar, peer, rx_tid, ba_win_sz, ssn, true); spin_unlock_bh(&ab->base_lock); @@ -986,8 +986,8 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ if (!ab->hw_params->reoq_lut_support) { ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, - paddr, tid, 1, - ba_win_sz); + paddr_aligned, tid, + 1, ba_win_sz); if (ret) { ath12k_warn(ab, "failed to setup peer rx reorder queuefor tid %d: %d\n", tid, ret); @@ -1021,18 +1021,18 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ ath12k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, ssn, pn_type); - paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, - DMA_BIDIRECTIONAL); + paddr_aligned = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, + DMA_BIDIRECTIONAL); - ret = dma_mapping_error(ab->dev, paddr); + ret = dma_mapping_error(ab->dev, paddr_aligned); if (ret) { spin_unlock_bh(&ab->base_lock); goto err_mem_free; } - rx_tid->vaddr = vaddr; - rx_tid->paddr = paddr; - rx_tid->size = hw_desc_sz; + rx_tid->qbuf.vaddr = vaddr; + rx_tid->qbuf.paddr_aligned = paddr_aligned; + rx_tid->qbuf.size = hw_desc_sz; rx_tid->active = true; if (ab->hw_params->reoq_lut_support) { @@ -1040,15 +1040,18 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ * and tid with qaddr. */ if (peer->mlo) - ath12k_peer_rx_tid_qref_setup(ab, peer->ml_id, tid, paddr); + ath12k_peer_rx_tid_qref_setup(ab, peer->ml_id, tid, + paddr_aligned); else - ath12k_peer_rx_tid_qref_setup(ab, peer->peer_id, tid, paddr); + ath12k_peer_rx_tid_qref_setup(ab, peer->peer_id, tid, + paddr_aligned); spin_unlock_bh(&ab->base_lock); } else { spin_unlock_bh(&ab->base_lock); ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, - paddr, tid, 1, ba_win_sz); + paddr_aligned, tid, 1, + ba_win_sz); } return ret; @@ -1191,8 +1194,8 @@ int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_link_vif *arvif, rx_tid = &peer->rx_tid[tid]; if (!rx_tid->active) continue; - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); ret = ath12k_dp_reo_cmd_send(ab, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, NULL); @@ -3254,8 +3257,9 @@ static int ath12k_dp_rx_h_defrag_reo_reinject(struct ath12k *ar, reo_ent_ring->queue_addr_lo = reo_dest_ring->rx_mpdu_info.peer_meta_data; queue_addr_hi = 0; } else { - reo_ent_ring->queue_addr_lo = cpu_to_le32(lower_32_bits(rx_tid->paddr)); - queue_addr_hi = upper_32_bits(rx_tid->paddr); + reo_ent_ring->queue_addr_lo = + cpu_to_le32(lower_32_bits(rx_tid->qbuf.paddr_aligned)); + queue_addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); } reo_ent_ring->info0 = le32_encode_bits(queue_addr_hi, diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index eb4d2b60a035..4d90fc98fd99 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -12,13 +12,17 @@ #define DP_MAX_NWIFI_HDR_LEN 30 +struct ath12k_reoq_buf { + void *vaddr; + dma_addr_t paddr_aligned; + u32 size; +}; + struct ath12k_dp_rx_tid { u8 tid; - u32 *vaddr; - dma_addr_t paddr; - u32 size; u32 ba_win_sz; bool active; + struct ath12k_reoq_buf qbuf; /* Info related to rx fragments */ u32 cur_sn; -- 2.51.0 From 3b9cbce6fdd3d511107e7eaf27a0f1e0d73038ea Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 9 Apr 2025 10:26:40 +0800 Subject: [PATCH 04/16] wifi: ath12k: alloc REO queue per station In MLO case, all link peers share the same REO queue, so the queue should be allocated only once, currently this is done by checking primary_link flag in ath12k_dp_rx_peer_tid_setup(). However, the check not only avoids duplicate allocation, but also bypasses sending queue configuration to firmware for non-primary links. In an upcoming patch, changes will be added to make this check no-ops for WCN7850, as WCN7850 firmware needs to be explicitly notified each link peer's queue configuration. That said, the duplicate allocation would arise again after that change, hence it needs to be resolved before hand. Since all link peers share the same queue, it should be allocated per MLD peer, not per link peer. So change to do allocation once and save it in MLD peer, link peers can simply get queue configuration from there. Also relocate ath12k_reoq_buf structure to core.h to avoid circular dependency. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00284-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00209-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang Link: https://patch.msgid.link/20250409-ath12k-wcn7850-mlo-support-v2-7-3801132ca2c3@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 8 +++ drivers/net/wireless/ath/ath12k/dp_rx.c | 95 +++++++++++++++---------- drivers/net/wireless/ath/ath12k/dp_rx.h | 6 -- 3 files changed, 65 insertions(+), 44 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 1bc2a391171d..0c8bb64ec08f 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -535,6 +535,12 @@ struct ath12k_link_sta { u8 link_idx; }; +struct ath12k_reoq_buf { + void *vaddr; + dma_addr_t paddr_aligned; + u32 size; +}; + struct ath12k_sta { struct ath12k_vif *ahvif; enum hal_pn_type pn_type; @@ -547,6 +553,8 @@ struct ath12k_sta { u8 num_peer; enum ieee80211_sta_state state; + + struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; }; #define ATH12K_HALF_20MHZ_BW 10 diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index acafaabc4c9d..a8d603bd9131 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -929,17 +929,66 @@ static int ath12k_peer_rx_tid_reo_update(struct ath12k *ar, return 0; } +static int ath12k_dp_rx_assign_reoq(struct ath12k_base *ab, + struct ath12k_sta *ahsta, + struct ath12k_dp_rx_tid *rx_tid, + u16 ssn, enum hal_pn_type pn_type) +{ + u32 ba_win_sz = rx_tid->ba_win_sz; + struct ath12k_reoq_buf *buf; + void *vaddr, *vaddr_aligned; + dma_addr_t paddr_aligned; + u8 tid = rx_tid->tid; + u32 hw_desc_sz; + int ret; + + buf = &ahsta->reoq_bufs[tid]; + if (!buf->vaddr) { + /* TODO: Optimize the memory allocation for qos tid based on + * the actual BA window size in REO tid update path. + */ + if (tid == HAL_DESC_REO_NON_QOS_TID) + hw_desc_sz = ath12k_hal_reo_qdesc_size(ba_win_sz, tid); + else + hw_desc_sz = ath12k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); + + vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC); + if (!vaddr) + return -ENOMEM; + + vaddr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); + + ath12k_hal_reo_qdesc_setup(vaddr_aligned, tid, ba_win_sz, + ssn, pn_type); + + paddr_aligned = dma_map_single(ab->dev, vaddr_aligned, hw_desc_sz, + DMA_BIDIRECTIONAL); + ret = dma_mapping_error(ab->dev, paddr_aligned); + if (ret) { + kfree(vaddr); + return ret; + } + + buf->vaddr = vaddr; + buf->paddr_aligned = paddr_aligned; + buf->size = hw_desc_sz; + } + + rx_tid->qbuf = *buf; + rx_tid->active = true; + + return 0; +} + int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id, u8 tid, u32 ba_win_sz, u16 ssn, enum hal_pn_type pn_type) { struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = &ab->dp; - struct hal_rx_reo_queue *addr_aligned; struct ath12k_peer *peer; + struct ath12k_sta *ahsta; struct ath12k_dp_rx_tid *rx_tid; - u32 hw_desc_sz; - void *vaddr; dma_addr_t paddr_aligned; int ret; @@ -972,9 +1021,9 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ } rx_tid = &peer->rx_tid[tid]; + paddr_aligned = rx_tid->qbuf.paddr_aligned; /* Update the tid queue if it is already setup */ if (rx_tid->active) { - paddr_aligned = rx_tid->qbuf.paddr_aligned; ret = ath12k_peer_rx_tid_reo_update(ar, peer, rx_tid, ba_win_sz, ssn, true); spin_unlock_bh(&ab->base_lock); @@ -1002,39 +1051,14 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ rx_tid->ba_win_sz = ba_win_sz; - /* TODO: Optimize the memory allocation for qos tid based on - * the actual BA window size in REO tid update path. - */ - if (tid == HAL_DESC_REO_NON_QOS_TID) - hw_desc_sz = ath12k_hal_reo_qdesc_size(ba_win_sz, tid); - else - hw_desc_sz = ath12k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); - - vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC); - if (!vaddr) { - spin_unlock_bh(&ab->base_lock); - return -ENOMEM; - } - - addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); - - ath12k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, - ssn, pn_type); - - paddr_aligned = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, - DMA_BIDIRECTIONAL); - - ret = dma_mapping_error(ab->dev, paddr_aligned); + ahsta = ath12k_sta_to_ahsta(peer->sta); + ret = ath12k_dp_rx_assign_reoq(ab, ahsta, rx_tid, ssn, pn_type); if (ret) { spin_unlock_bh(&ab->base_lock); - goto err_mem_free; + ath12k_warn(ab, "failed to assign reoq buf for rx tid %u\n", tid); + return ret; } - rx_tid->qbuf.vaddr = vaddr; - rx_tid->qbuf.paddr_aligned = paddr_aligned; - rx_tid->qbuf.size = hw_desc_sz; - rx_tid->active = true; - if (ab->hw_params->reoq_lut_support) { /* Update the REO queue LUT at the corresponding peer id * and tid with qaddr. @@ -1054,11 +1078,6 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ ba_win_sz); } - return ret; - -err_mem_free: - kfree(vaddr); - return ret; } diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 4d90fc98fd99..19fb41e1c32b 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -12,12 +12,6 @@ #define DP_MAX_NWIFI_HDR_LEN 30 -struct ath12k_reoq_buf { - void *vaddr; - dma_addr_t paddr_aligned; - u32 size; -}; - struct ath12k_dp_rx_tid { u8 tid; u32 ba_win_sz; -- 2.51.0 From bcdf2620f95752af542c18ee81a83231d4993953 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 9 Apr 2025 10:26:41 +0800 Subject: [PATCH 05/16] wifi: ath12k: don't skip non-primary links for WCN7850 The primary link check bypasses REO queue setup for non-primary links in ath12k_dp_rx_peer_tid_setup(), this works for QCN9274 but breaks WCN7850 as WCN7850 needs it to be done for each link peer. Add a new hardware parameter to differentiate them. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00284-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00209-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang Link: https://patch.msgid.link/20250409-ath12k-wcn7850-mlo-support-v2-8-3801132ca2c3@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_rx.c | 3 ++- drivers/net/wireless/ath/ath12k/hw.c | 8 ++++++++ drivers/net/wireless/ath/ath12k/hw.h | 3 +++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index a8d603bd9131..37a82419aaf2 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1001,7 +1001,8 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ return -ENOENT; } - if (!peer->primary_link) { + if (ab->hw_params->dp_primary_link_only && + !peer->primary_link) { spin_unlock_bh(&ab->base_lock); return 0; } diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index e3249719ca53..a46d82857c5d 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1327,6 +1327,8 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .bdf_addr_offset = 0, .current_cc_support = false, + + .dp_primary_link_only = true, }, { .name = "wcn7850 hw2.0", @@ -1414,6 +1416,8 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .bdf_addr_offset = 0, .current_cc_support = true, + + .dp_primary_link_only = false, }, { .name = "qcn9274 hw2.0", @@ -1497,6 +1501,8 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .bdf_addr_offset = 0, .current_cc_support = false, + + .dp_primary_link_only = true, }, { .name = "ipq5332 hw1.0", @@ -1573,6 +1579,8 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .ce_ie_addr = &ath12k_ce_ie_addr_ipq5332, .ce_remap = &ath12k_ce_remap_ipq5332, .bdf_addr_offset = 0xC00000, + + .dp_primary_link_only = true, }, }; diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index d0218d3eea54..024cfcd2cc15 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -233,6 +233,9 @@ struct ath12k_hw_params { const struct ce_ie_addr *ce_ie_addr; const struct ce_remap *ce_remap; u32 bdf_addr_offset; + + /* setup REO queue, frag etc only for primary link peer */ + bool dp_primary_link_only:1; }; struct ath12k_hw_ops { -- 2.51.0 From 176f3009ae598d0523b267db319fe16f69577231 Mon Sep 17 00:00:00 2001 From: Baochen Qiang Date: Wed, 9 Apr 2025 10:26:42 +0800 Subject: [PATCH 06/16] wifi: ath12k: support 2 channels for single pdev device For single pdev device, radio number of a device is forced as 1 in ath12k_wmi_ext_soc_hal_reg_caps_parse(). This leads to ah->num_radio == 1 and then in ath12k_mac_setup_iface_combinations() we report to mac/cfg80211 that only 1 channel is supported. In MLO case, it finally results in failing to bring up the second link as it is in another channel. Change num_different_channels to 2 to allow a second link. Since DFS on multiple channels are not supported yet, remove radar_detect_widths. For now WCN7850 is the only single pdev device, so others should not be affected. Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00284-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1 Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00209-QCAHKSWPL_SILICONZ-1 Signed-off-by: Baochen Qiang Link: https://patch.msgid.link/20250409-ath12k-wcn7850-mlo-support-v2-9-3801132ca2c3@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 48d60f7b891a..dfc803879028 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -11089,13 +11089,18 @@ ath12k_mac_setup_radio_iface_comb(struct ath12k *ar, comb[0].limits = limits; comb[0].n_limits = n_limits; comb[0].max_interfaces = max_interfaces; - comb[0].num_different_channels = 1; comb[0].beacon_int_infra_match = true; comb[0].beacon_int_min_gcd = 100; - comb[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40) | - BIT(NL80211_CHAN_WIDTH_80); + + if (ar->ab->hw_params->single_pdev_only) { + comb[0].num_different_channels = 2; + } else { + comb[0].num_different_channels = 1; + comb[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80); + } return 0; } -- 2.51.0 From 37a068fc9dc4feb8d76e8896bb33883d06c11a6b Mon Sep 17 00:00:00 2001 From: P Praneesh Date: Fri, 11 Apr 2025 11:31:51 +0530 Subject: [PATCH 07/16] wifi: ath12k: Handle error cases during extended skb allocation Currently, in the case of extended skb allocation, the buffer is freed before the DMA unmap operation. This premature deletion can result in skb->data corruption, as the memory region could be re-allocated for other purposes. Fix this issue by reordering the failure cases by calling dma_unmap_single() first, then followed by the corresponding kfree_skb(). This helps avoid data corruption in case of failures in dp_tx(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250411060154.1388159-2-praneesh.p@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_tx.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 91da0805706f..9d58a9a9cbff 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -231,7 +231,7 @@ int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb); struct hal_tcl_data_cmd *hal_tcl_desc; struct hal_tx_msdu_ext_desc *msg; - struct sk_buff *skb_ext_desc; + struct sk_buff *skb_ext_desc = NULL; struct hal_srng *tcl_ring; struct ieee80211_hdr *hdr = (void *)skb->data; struct ath12k_vif *ahvif = arvif->ahvif; @@ -417,18 +417,15 @@ map: if (ret < 0) { ath12k_dbg(ab, ATH12K_DBG_DP_TX, "Failed to add HTT meta data, dropping packet\n"); - kfree_skb(skb_ext_desc); - goto fail_unmap_dma; + goto fail_free_ext_skb; } } ti.paddr = dma_map_single(ab->dev, skb_ext_desc->data, skb_ext_desc->len, DMA_TO_DEVICE); ret = dma_mapping_error(ab->dev, ti.paddr); - if (ret) { - kfree_skb(skb_ext_desc); - goto fail_unmap_dma; - } + if (ret) + goto fail_free_ext_skb; ti.data_len = skb_ext_desc->len; ti.type = HAL_TCL_DESC_TYPE_EXT_DESC; @@ -464,7 +461,7 @@ map: ring_selector++; } - goto fail_unmap_dma; + goto fail_unmap_dma_ext; } spin_lock_bh(&arvif->link_stats_lock); @@ -491,13 +488,16 @@ map: return 0; -fail_unmap_dma: - dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); - +fail_unmap_dma_ext: if (skb_cb->paddr_ext_desc) dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); +fail_free_ext_skb: + kfree_skb(skb_ext_desc); + +fail_unmap_dma: + dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); fail_remove_tx_buf: ath12k_dp_tx_release_txbuf(dp, tx_desc, pool_id); -- 2.51.0 From 10ec3d54df783bc9585b98559a1f556019312462 Mon Sep 17 00:00:00 2001 From: P Praneesh Date: Fri, 11 Apr 2025 11:31:52 +0530 Subject: [PATCH 08/16] wifi: ath12k: Refactor tx descriptor handling in tx completion handler Current code uses mac_id and msdu parameters in ath12k_dp_tx_free_txbuf() and ath12k_dp_tx_process_htt_tx_complete(). Since these parameters are already encapsulated by struct ath12k_dp_tx_desc, passing them individually results in redundant arguments. Introduce struct ath12k_tx_desc_params to capture the skb, mac_id, and pass it to the corresponding functions. Refactor these functions to use struct ath12k_tx_desc_params instead, reducing the number of arguments and improving function argument handling efficiency. Additionally, use struct ath12k_tx_desc_params in ath12k_dp_tx_htt_tx_complete_buf() and ath12k_dp_tx_complete_msdu(), which will be utilized for fetching extended skb in a future patch. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250411060154.1388159-3-praneesh.p@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.h | 5 +++ drivers/net/wireless/ath/ath12k/dp_tx.c | 42 ++++++++++++------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 6ce5f853dcdb..241da2a3bd34 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -301,6 +301,11 @@ struct ath12k_tx_desc_info { u8 pool_id; }; +struct ath12k_tx_desc_params { + struct sk_buff *skb; + u8 mac_id; +}; + struct ath12k_spt_info { dma_addr_t paddr; u64 *vaddr; diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 9d58a9a9cbff..a272d5b8a51c 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -513,12 +513,13 @@ fail_remove_tx_buf: } static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab, - struct sk_buff *msdu, u8 mac_id, - struct dp_tx_ring *tx_ring) + struct dp_tx_ring *tx_ring, + struct ath12k_tx_desc_params *desc_params) { struct ath12k *ar; + struct sk_buff *msdu = desc_params->skb; struct ath12k_skb_cb *skb_cb; - u8 pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id); + u8 pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, desc_params->mac_id); skb_cb = ATH12K_SKB_CB(msdu); ar = ab->pdevs[pdev_id].ar; @@ -536,7 +537,7 @@ static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab, static void ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, - struct sk_buff *msdu, + struct ath12k_tx_desc_params *desc_params, struct dp_tx_ring *tx_ring, struct ath12k_dp_htt_wbm_tx_status *ts) { @@ -546,6 +547,7 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, struct ieee80211_vif *vif; struct ath12k_vif *ahvif; struct ath12k *ar; + struct sk_buff *msdu = desc_params->skb; skb_cb = ATH12K_SKB_CB(msdu); info = IEEE80211_SKB_CB(msdu); @@ -594,10 +596,9 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, } static void -ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, - void *desc, u8 mac_id, - struct sk_buff *msdu, - struct dp_tx_ring *tx_ring) +ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, void *desc, + struct dp_tx_ring *tx_ring, + struct ath12k_tx_desc_params *desc_params) { struct htt_tx_wbm_completion *status_desc; struct ath12k_dp_htt_wbm_tx_status ts = {0}; @@ -613,14 +614,14 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, ts.acked = (wbm_status == HAL_WBM_REL_HTT_TX_COMP_STATUS_OK); ts.ack_rssi = le32_get_bits(status_desc->info2, HTT_TX_WBM_COMP_INFO2_ACK_RSSI); - ath12k_dp_tx_htt_tx_complete_buf(ab, msdu, tx_ring, &ts); + ath12k_dp_tx_htt_tx_complete_buf(ab, desc_params, tx_ring, &ts); break; case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: case HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ: case HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT: case HAL_WBM_REL_HTT_TX_COMP_STATUS_VDEVID_MISMATCH: - ath12k_dp_tx_free_txbuf(ab, msdu, mac_id, tx_ring); + ath12k_dp_tx_free_txbuf(ab, tx_ring, desc_params); break; case HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY: /* This event is to be handled only when the driver decides to @@ -752,7 +753,7 @@ static void ath12k_dp_tx_update_txcompl(struct ath12k *ar, struct hal_tx_status } static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, - struct sk_buff *msdu, + struct ath12k_tx_desc_params *desc_params, struct hal_tx_status *ts) { struct ath12k_base *ab = ar->ab; @@ -762,6 +763,7 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, struct ath12k_skb_cb *skb_cb; struct ieee80211_vif *vif; struct ath12k_vif *ahvif; + struct sk_buff *msdu = desc_params->skb; if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) { /* Must not happen */ @@ -891,11 +893,11 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id; struct hal_srng *status_ring = &ab->hal.srng_list[hal_ring_id]; struct ath12k_tx_desc_info *tx_desc = NULL; - struct sk_buff *msdu; struct hal_tx_status ts = { 0 }; + struct ath12k_tx_desc_params desc_params; struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; struct hal_wbm_release_ring *desc; - u8 mac_id, pdev_id; + u8 pdev_id; u64 desc_va; spin_lock_bh(&status_ring->lock); @@ -949,28 +951,26 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) continue; } - msdu = tx_desc->skb; - mac_id = tx_desc->mac_id; + desc_params.mac_id = tx_desc->mac_id; + desc_params.skb = tx_desc->skb; /* Release descriptor as soon as extracting necessary info * to reduce contention */ ath12k_dp_tx_release_txbuf(dp, tx_desc, tx_desc->pool_id); if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) { - ath12k_dp_tx_process_htt_tx_complete(ab, - (void *)tx_status, - mac_id, msdu, - tx_ring); + ath12k_dp_tx_process_htt_tx_complete(ab, (void *)tx_status, + tx_ring, &desc_params); continue; } - pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id); + pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, desc_params.mac_id); ar = ab->pdevs[pdev_id].ar; if (atomic_dec_and_test(&ar->dp.num_tx_pending)) wake_up(&ar->dp.tx_empty_waitq); - ath12k_dp_tx_complete_msdu(ar, msdu, &ts); + ath12k_dp_tx_complete_msdu(ar, &desc_params, &ts); } } -- 2.51.0 From 1e326ada3586277faa1f67e9263fbc09311986a6 Mon Sep 17 00:00:00 2001 From: P Praneesh Date: Fri, 11 Apr 2025 11:31:53 +0530 Subject: [PATCH 09/16] wifi: ath12k: Fix memory leak during extended skb allocation In ath12k_dp_tx(), memory allocated for extended skb is not freed properly, causing a memory leak even when the host receives tx completion for those skbs. Fix this issue by storing skb_ext_desc in the host tx descriptor and using this skb_ext_desc field during completion or during ath12k_dp_cc_cleanup(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250411060154.1388159-4-praneesh.p@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp.c | 10 +++++++++- drivers/net/wireless/ath/ath12k/dp.h | 2 ++ drivers/net/wireless/ath/ath12k/dp_tx.c | 15 ++++++++++++--- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 59f61341383a..e04415517860 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -1206,11 +1206,19 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) if (!skb) continue; + skb_cb = ATH12K_SKB_CB(skb); + if (skb_cb->paddr_ext_desc) { + dma_unmap_single(ab->dev, + skb_cb->paddr_ext_desc, + tx_desc_info->skb_ext_desc->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(tx_desc_info->skb_ext_desc); + } + /* if we are unregistering, hw would've been destroyed and * ar is no longer valid. */ if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) { - skb_cb = ATH12K_SKB_CB(skb); ar = skb_cb->ar; if (atomic_dec_and_test(&ar->dp.num_tx_pending)) diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 241da2a3bd34..706d766d8c81 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -296,6 +296,7 @@ struct ath12k_rx_desc_info { struct ath12k_tx_desc_info { struct list_head list; struct sk_buff *skb; + struct sk_buff *skb_ext_desc; u32 desc_id; /* Cookie */ u8 mac_id; u8 pool_id; @@ -303,6 +304,7 @@ struct ath12k_tx_desc_info { struct ath12k_tx_desc_params { struct sk_buff *skb; + struct sk_buff *skb_ext_desc; u8 mac_id; }; diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index a272d5b8a51c..9bcd20712888 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -84,6 +84,7 @@ static void ath12k_dp_tx_release_txbuf(struct ath12k_dp *dp, u8 pool_id) { spin_lock_bh(&dp->tx_desc_lock[pool_id]); + tx_desc->skb_ext_desc = NULL; list_move_tail(&tx_desc->list, &dp->tx_desc_free_list[pool_id]); spin_unlock_bh(&dp->tx_desc_lock[pool_id]); } @@ -431,6 +432,7 @@ map: ti.type = HAL_TCL_DESC_TYPE_EXT_DESC; skb_cb->paddr_ext_desc = ti.paddr; + tx_desc->skb_ext_desc = skb_ext_desc; } hal_ring_id = tx_ring->tcl_data_ring.ring_id; @@ -525,9 +527,11 @@ static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab, ar = ab->pdevs[pdev_id].ar; dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + dev_kfree_skb_any(desc_params->skb_ext_desc); + } ieee80211_free_txskb(ar->ah->hw, msdu); @@ -558,9 +562,11 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, wake_up(&ar->dp.tx_empty_waitq); dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + dev_kfree_skb_any(desc_params->skb_ext_desc); + } vif = skb_cb->vif; if (vif) { @@ -773,9 +779,11 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, skb_cb = ATH12K_SKB_CB(msdu); dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + dev_kfree_skb_any(desc_params->skb_ext_desc); + } rcu_read_lock(); @@ -953,6 +961,7 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) desc_params.mac_id = tx_desc->mac_id; desc_params.skb = tx_desc->skb; + desc_params.skb_ext_desc = tx_desc->skb_ext_desc; /* Release descriptor as soon as extracting necessary info * to reduce contention -- 2.51.0 From 65bf4ed2db9ac3590c7f0545b6bbebbacbfd974f Mon Sep 17 00:00:00 2001 From: P Praneesh Date: Fri, 11 Apr 2025 11:31:54 +0530 Subject: [PATCH 10/16] wifi: ath12k: Use skb->len for dma_unmap_single() length parameter During dma_unmap_single() for extended skb, the driver currently uses sizeof() of the corresponding structure. When the allocation size changes, one of the parameters of dma_unmap_single() needs to be updated everywhere. Improve code readability by using skb->len instead of sizeof() the structure. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: P Praneesh Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250411060154.1388159-5-praneesh.p@oss.qualcomm.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_tx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index 9bcd20712888..2136eeb278af 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -493,7 +493,7 @@ map: fail_unmap_dma_ext: if (skb_cb->paddr_ext_desc) dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), + skb_ext_desc->len, DMA_TO_DEVICE); fail_free_ext_skb: kfree_skb(skb_ext_desc); @@ -529,7 +529,7 @@ static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab, dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + desc_params->skb_ext_desc->len, DMA_TO_DEVICE); dev_kfree_skb_any(desc_params->skb_ext_desc); } @@ -564,7 +564,7 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + desc_params->skb_ext_desc->len, DMA_TO_DEVICE); dev_kfree_skb_any(desc_params->skb_ext_desc); } @@ -781,7 +781,7 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + desc_params->skb_ext_desc->len, DMA_TO_DEVICE); dev_kfree_skb_any(desc_params->skb_ext_desc); } -- 2.51.0 From b7544de8a2984e61b95c58c1c6c1e8ce659b1021 Mon Sep 17 00:00:00 2001 From: Rajat Soni Date: Tue, 8 Apr 2025 09:51:26 +0530 Subject: [PATCH 11/16] wifi: ath12k: Add helper function ath12k_mac_update_freq_range() In a subsequent change, the frequency range needs to be updated with each regulatory update. Since the current function also modifies the DISABLED flag in the actual channel list, which should always align with hardware-supported start and end frequencies rather than the current operating ones, the existing function cannot be called again. Therefore, the logic for setting the frequency range needs to be refactored. To address this, refactor the frequency setting part into a new helper function, ath12k_mac_update_freq_range(). Since this needs to be done independently of updating the DISABLED flag, call the new helper function ath12k_mac_update_freq_range() after updating the channel list for each band. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Rajat Soni Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250408042128.720263-2-quic_rajson@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/mac.c | 22 +++++++++++++++++++--- drivers/net/wireless/ath/ath12k/mac.h | 2 ++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index dfc803879028..660dd4b17fe1 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10865,6 +10865,16 @@ static const struct ieee80211_ops ath12k_ops = { #endif }; +void ath12k_mac_update_freq_range(struct ath12k *ar, + u32 freq_low, u32 freq_high) +{ + if (!(freq_low && freq_high)) + return; + + ar->freq_range.start_freq = MHZ_TO_KHZ(freq_low); + ar->freq_range.end_freq = MHZ_TO_KHZ(freq_high); +} + static void ath12k_mac_update_ch_list(struct ath12k *ar, struct ieee80211_supported_band *band, u32 freq_low, u32 freq_high) @@ -10879,9 +10889,6 @@ static void ath12k_mac_update_ch_list(struct ath12k *ar, band->channels[i].center_freq > freq_high) band->channels[i].flags |= IEEE80211_CHAN_DISABLED; } - - ar->freq_range.start_freq = MHZ_TO_KHZ(freq_low); - ar->freq_range.end_freq = MHZ_TO_KHZ(freq_high); } static u32 ath12k_get_phy_id(struct ath12k *ar, u32 band) @@ -10939,6 +10946,9 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, ath12k_mac_update_ch_list(ar, band, reg_cap->low_2ghz_chan, reg_cap->high_2ghz_chan); + + ath12k_mac_update_freq_range(ar, reg_cap->low_2ghz_chan, + reg_cap->high_2ghz_chan); } if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { @@ -10961,6 +10971,9 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, ath12k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); + + ath12k_mac_update_freq_range(ar, reg_cap->low_5ghz_chan, + reg_cap->high_5ghz_chan); ah->use_6ghz_regd = true; } @@ -10990,6 +11003,9 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, ath12k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); + + ath12k_mac_update_freq_range(ar, reg_cap->low_5ghz_chan, + reg_cap->high_5ghz_chan); } } diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 6c5f9d587a60..da37332352fe 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -126,4 +126,6 @@ struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 link_id); int ath12k_mac_get_fw_stats(struct ath12k *ar, struct ath12k_fw_stats_req_params *param); +void ath12k_mac_update_freq_range(struct ath12k *ar, + u32 freq_low, u32 freq_high); #endif -- 2.51.0 From 657b0c72c4ad688d424560af98e3258b3f875a4d Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 8 Apr 2025 09:51:27 +0530 Subject: [PATCH 12/16] wifi: ath12k: Fix frequency range in driver During the initial WMI exchange, the firmware updates the hardware-supported start and end frequencies in the ath12k_wmi_hal_reg_capabilities_ext_arg structure. These frequencies, being hardware-supported, may not always align with the current regulatory operating frequencies. When operating as multiple grouped hardwares under a single wiphy, the driver advertises these values directly to the upper layer in the per-radio frequency range, which can be misleading. Sample output snippet from iw phyX info command - [..] Supported wiphy radios: * Idx 0: Frequency Range: 2312 MHz - 2732 MHz [..] * Idx 1: Frequency Range: 5150 MHz - 5330 MHz [..] [..] The frequency range displayed above is incorrect because the driver directly advertises the hardware-supported values to the upper layer. The driver is aware of the current operating regulatory rules, and hence it can use this information to determine the final operating start and end frequencies. To resolve this issue, add support to store the start and end frequencies received during the regulatory update event. Then, intersect these with the hardware-supported start and end frequencies, and finally, advertise the intersected values to the upper layer. Sample output snippet from iw phyX info command after the fix - [..] Supported wiphy radios: * Idx 0: Frequency Range: 2402 MHz - 2472 MHz [..] * Idx 1: Frequency Range: 5170 MHz - 5330 MHz [..] [..] Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Aditya Kumar Singh Co-developed-by: Rajat Soni Signed-off-by: Rajat Soni Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250408042128.720263-3-quic_rajson@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/core.h | 9 ++++++ drivers/net/wireless/ath/ath12k/mac.c | 39 ++++++++++++++++++-------- drivers/net/wireless/ath/ath12k/reg.c | 13 +++++++++ 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index 0c8bb64ec08f..4b8f434e3e9a 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -893,6 +893,11 @@ struct ath12k_soc_dp_stats { struct ath12k_soc_dp_tx_err_stats tx_err; }; +struct ath12k_reg_freq { + u32 start_freq; + u32 end_freq; +}; + struct ath12k_mlo_memory { struct target_mem_chunk chunk[ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; int mlo_mem_size; @@ -1140,6 +1145,10 @@ struct ath12k_base { /* Denote whether MLO is possible within the device */ bool single_chip_mlo_support; + struct ath12k_reg_freq reg_freq_2ghz; + struct ath12k_reg_freq reg_freq_5ghz; + struct ath12k_reg_freq reg_freq_6ghz; + /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 660dd4b17fe1..6e4619e5cdf1 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -10913,16 +10913,17 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, { struct ieee80211_supported_band *band; struct ath12k_wmi_hal_reg_capabilities_ext_arg *reg_cap; + struct ath12k_base *ab = ar->ab; + u32 phy_id, freq_low, freq_high; struct ath12k_hw *ah = ar->ah; void *channels; - u32 phy_id; BUILD_BUG_ON((ARRAY_SIZE(ath12k_2ghz_channels) + ARRAY_SIZE(ath12k_5ghz_channels) + ARRAY_SIZE(ath12k_6ghz_channels)) != ATH12K_NUM_CHANS); - reg_cap = &ar->ab->hal_reg_cap[ar->pdev_idx]; + reg_cap = &ab->hal_reg_cap[ar->pdev_idx]; if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { channels = kmemdup(ath12k_2ghz_channels, @@ -10939,16 +10940,21 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->bitrates = ath12k_g_rates; bands[NL80211_BAND_2GHZ] = band; - if (ar->ab->hw_params->single_pdev_only) { + if (ab->hw_params->single_pdev_only) { phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_2GHZ_CAP); - reg_cap = &ar->ab->hal_reg_cap[phy_id]; + reg_cap = &ab->hal_reg_cap[phy_id]; } + + freq_low = max(reg_cap->low_2ghz_chan, + ab->reg_freq_2ghz.start_freq); + freq_high = min(reg_cap->high_2ghz_chan, + ab->reg_freq_2ghz.end_freq); + ath12k_mac_update_ch_list(ar, band, reg_cap->low_2ghz_chan, reg_cap->high_2ghz_chan); - ath12k_mac_update_freq_range(ar, reg_cap->low_2ghz_chan, - reg_cap->high_2ghz_chan); + ath12k_mac_update_freq_range(ar, freq_low, freq_high); } if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { @@ -10968,12 +10974,17 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->n_bitrates = ath12k_a_rates_size; band->bitrates = ath12k_a_rates; bands[NL80211_BAND_6GHZ] = band; + + freq_low = max(reg_cap->low_5ghz_chan, + ab->reg_freq_6ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, + ab->reg_freq_6ghz.end_freq); + ath12k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); - ath12k_mac_update_freq_range(ar, reg_cap->low_5ghz_chan, - reg_cap->high_5ghz_chan); + ath12k_mac_update_freq_range(ar, freq_low, freq_high); ah->use_6ghz_regd = true; } @@ -10995,17 +11006,21 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->bitrates = ath12k_a_rates; bands[NL80211_BAND_5GHZ] = band; - if (ar->ab->hw_params->single_pdev_only) { + if (ab->hw_params->single_pdev_only) { phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5GHZ_CAP); - reg_cap = &ar->ab->hal_reg_cap[phy_id]; + reg_cap = &ab->hal_reg_cap[phy_id]; } + freq_low = max(reg_cap->low_5ghz_chan, + ab->reg_freq_5ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, + ab->reg_freq_5ghz.end_freq); + ath12k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); - ath12k_mac_update_freq_range(ar, reg_cap->low_5ghz_chan, - reg_cap->high_5ghz_chan); + ath12k_mac_update_freq_range(ar, freq_low, freq_high); } } diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index 3260df2ad600..319503244ea4 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -651,6 +651,16 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, *rule_idx = i; } +static void ath12k_reg_update_freq_range(struct ath12k_reg_freq *reg_freq, + struct ath12k_reg_rule *reg_rule) +{ + if (reg_freq->start_freq > reg_rule->start_freq) + reg_freq->start_freq = reg_rule->start_freq; + + if (reg_freq->end_freq < reg_rule->end_freq) + reg_freq->end_freq = reg_rule->end_freq; +} + struct ieee80211_regdomain * ath12k_reg_build_regd(struct ath12k_base *ab, struct ath12k_reg_info *reg_info, bool intersect) @@ -704,6 +714,7 @@ ath12k_reg_build_regd(struct ath12k_base *ab, max_bw = min_t(u16, reg_rule->max_bw, reg_info->max_bw_2g); flags = 0; + ath12k_reg_update_freq_range(&ab->reg_freq_2ghz, reg_rule); } else if (reg_info->num_5g_reg_rules && (j < reg_info->num_5g_reg_rules)) { reg_rule = reg_info->reg_rules_5g_ptr + j++; @@ -717,6 +728,7 @@ ath12k_reg_build_regd(struct ath12k_base *ab, * per other BW rule flags we pass from here */ flags = NL80211_RRF_AUTO_BW; + ath12k_reg_update_freq_range(&ab->reg_freq_5ghz, reg_rule); } else if (reg_info->is_ext_reg_event && reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP] && (k < reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP])) { @@ -724,6 +736,7 @@ ath12k_reg_build_regd(struct ath12k_base *ab, max_bw = min_t(u16, reg_rule->max_bw, reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]); flags = NL80211_RRF_AUTO_BW; + ath12k_reg_update_freq_range(&ab->reg_freq_6ghz, reg_rule); } else { break; } -- 2.51.0 From 13324cecbb2c390a11f1fbfe87f3a5e62d6e4591 Mon Sep 17 00:00:00 2001 From: Aditya Kumar Singh Date: Tue, 8 Apr 2025 09:51:28 +0530 Subject: [PATCH 13/16] wifi: ath12k: Update frequency range if reg rules changes During the hardware register, driver updates the frequency range during boot-up. However, if new regulatory rules are applied after boot-up, the frequency range remains based on the older rules. Since different countries have varying regulatory rules, the frequency range can differ. Retaining the frequency range based on outdated rules can be misleading. Update the frequency range according to the new regulatory rules in the function ath12k_regd_update(). Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 Signed-off-by: Aditya Kumar Singh Co-developed-by: Rajat Soni Signed-off-by: Rajat Soni Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250408042128.720263-4-quic_rajson@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/reg.c | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index 319503244ea4..9d188e1037a6 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -6,6 +6,7 @@ #include #include "core.h" #include "debug.h" +#include "mac.h" /* World regdom to be used in case default regd from fw is unavailable */ #define ATH12K_2GHZ_CH01_11 REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0) @@ -246,6 +247,8 @@ static void ath12k_copy_regd(struct ieee80211_regdomain *regd_orig, int ath12k_regd_update(struct ath12k *ar, bool init) { + u32 phy_id, freq_low = 0, freq_high = 0, supported_bands, band; + struct ath12k_wmi_hal_reg_capabilities_ext_arg *reg_cap; struct ath12k_hw *ah = ath12k_ar_to_ah(ar); struct ieee80211_hw *hw = ah->hw; struct ieee80211_regdomain *regd, *regd_copy = NULL; @@ -255,6 +258,47 @@ int ath12k_regd_update(struct ath12k *ar, bool init) ab = ar->ab; + supported_bands = ar->pdev->cap.supported_bands; + if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { + band = NL80211_BAND_2GHZ; + } else if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && !ar->supports_6ghz) { + band = NL80211_BAND_5GHZ; + } else if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) { + band = NL80211_BAND_6GHZ; + } else { + /* This condition is not expected. + */ + WARN_ON(1); + ret = -EINVAL; + goto err; + } + + reg_cap = &ab->hal_reg_cap[ar->pdev_idx]; + + if (ab->hw_params->single_pdev_only && !ar->supports_6ghz) { + phy_id = ar->pdev->cap.band[band].phy_id; + reg_cap = &ab->hal_reg_cap[phy_id]; + } + + /* Possible that due to reg change, current limits for supported + * frequency changed. Update that + */ + if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { + freq_low = max(reg_cap->low_2ghz_chan, ab->reg_freq_2ghz.start_freq); + freq_high = min(reg_cap->high_2ghz_chan, ab->reg_freq_2ghz.end_freq); + } else if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && !ar->supports_6ghz) { + freq_low = max(reg_cap->low_5ghz_chan, ab->reg_freq_5ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, ab->reg_freq_5ghz.end_freq); + } else if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) { + freq_low = max(reg_cap->low_5ghz_chan, ab->reg_freq_6ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, ab->reg_freq_6ghz.end_freq); + } + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); + + ath12k_dbg(ab, ATH12K_DBG_REG, "pdev %u reg updated freq limits %u->%u MHz\n", + ar->pdev->pdev_id, freq_low, freq_high); + /* If one of the radios within ah has already updated the regd for * the wiphy, then avoid setting regd again */ @@ -704,6 +748,16 @@ ath12k_reg_build_regd(struct ath12k_base *ab, "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", alpha2, ath12k_reg_get_regdom_str(tmp_regd->dfs_region), reg_info->dfs_region, num_rules); + + /* Reset start and end frequency for each band + */ + ab->reg_freq_5ghz.start_freq = INT_MAX; + ab->reg_freq_5ghz.end_freq = 0; + ab->reg_freq_2ghz.start_freq = INT_MAX; + ab->reg_freq_2ghz.end_freq = 0; + ab->reg_freq_6ghz.start_freq = INT_MAX; + ab->reg_freq_6ghz.end_freq = 0; + /* Update reg_rules[] below. Firmware is expected to * send these rules in order(2G rules first and then 5G) */ -- 2.51.0 From cb7433cc5cd4d07175dbc41f5a19966e9fae48be Mon Sep 17 00:00:00 2001 From: Sarika Sharma Date: Fri, 11 Apr 2025 11:45:22 +0530 Subject: [PATCH 14/16] wifi: ath12k: using msdu end descriptor to check for rx multicast packets Currently, the RX multicast broadcast packet check is performed using bit 15 from the info6 field of the MPDU start descriptor. This check can also be done using bit 9 from the info5 field of the MSDU end descriptor. However, in some scenarios multicast bit is not set when fetched from MPDU start descriptor. Therefore, checking the RX multicast broadcast packet from the MSDU end descriptor is more reliable as it is per MSDU. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Signed-off-by: Sarika Sharma Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250411061523.859387-2-quic_sarishar@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/hal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index d00869a33fea..12d0f991a47f 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -449,8 +449,8 @@ static u8 *ath12k_hw_qcn9274_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) static bool ath12k_hw_qcn9274_rx_desc_is_da_mcbc(struct hal_rx_desc *desc) { - return __le32_to_cpu(desc->u.qcn9274.mpdu_start.info6) & - RX_MPDU_START_INFO6_MCAST_BCAST; + return __le16_to_cpu(desc->u.qcn9274.msdu_end.info5) & + RX_MSDU_END_INFO5_DA_IS_MCBC; } static void ath12k_hw_qcn9274_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc, @@ -902,8 +902,8 @@ static u8 *ath12k_hw_qcn9274_compact_rx_desc_mpdu_start_addr2(struct hal_rx_desc static bool ath12k_hw_qcn9274_compact_rx_desc_is_da_mcbc(struct hal_rx_desc *desc) { - return __le32_to_cpu(desc->u.qcn9274_compact.mpdu_start.info6) & - RX_MPDU_START_INFO6_MCAST_BCAST; + return __le16_to_cpu(desc->u.qcn9274_compact.msdu_end.info5) & + RX_MSDU_END_INFO5_DA_IS_MCBC; } static void ath12k_hw_qcn9274_compact_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc, -- 2.51.0 From 4541b0c8c3c1b85564971d497224e57cf8076a02 Mon Sep 17 00:00:00 2001 From: Sarika Sharma Date: Fri, 11 Apr 2025 11:45:23 +0530 Subject: [PATCH 15/16] wifi: ath12k: correctly handle mcast packets for clients Currently, RX is_mcbc bit is set for packets sent from client as destination address (DA) is multicast/broadcast address, but packets are actually unicast as receiver address (RA) is not multicast address. Hence, packets are not handled properly due to this is_mcbc bit. Therefore, reset the is_mcbc bit if interface type is AP. Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1 Signed-off-by: Sarika Sharma Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250411061523.859387-3-quic_sarishar@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_rx.c | 5 +++++ drivers/net/wireless/ath/ath12k/peer.c | 5 ++++- drivers/net/wireless/ath/ath12k/peer.h | 3 ++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 37a82419aaf2..40feaa6d512d 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -2282,6 +2282,11 @@ static void ath12k_dp_rx_h_mpdu(struct ath12k *ar, spin_lock_bh(&ar->ab->base_lock); peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu, rx_info); if (peer) { + /* resetting mcbc bit because mcbc packets are unicast + * packets only for AP as STA sends unicast packets. + */ + rxcb->is_mcbc = rxcb->is_mcbc && !peer->ucast_ra_only; + if (rxcb->is_mcbc) enctype = peer->sec_type_grp; else diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 792cca8a3fb1..ec7236bbccc0 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -383,6 +383,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, arvif->ast_idx = peer->hw_peer_id; } + if (vif->type == NL80211_IFTYPE_AP) + peer->ucast_ra_only = true; + if (sta) { ahsta = ath12k_sta_to_ahsta(sta); arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, diff --git a/drivers/net/wireless/ath/ath12k/peer.h b/drivers/net/wireless/ath/ath12k/peer.h index 5870ee11a8c7..f3a5e054d2b5 100644 --- a/drivers/net/wireless/ath/ath12k/peer.h +++ b/drivers/net/wireless/ath/ath12k/peer.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_PEER_H @@ -62,6 +62,7 @@ struct ath12k_peer { /* for reference to ath12k_link_sta */ u8 link_id; + bool ucast_ra_only; }; struct ath12k_ml_peer { -- 2.51.0 From 9f17747fbda6fca934854463873c4abf8061491d Mon Sep 17 00:00:00 2001 From: Sarika Sharma Date: Tue, 8 Apr 2025 10:23:27 +0530 Subject: [PATCH 16/16] wifi: ath12k: fix invalid access to memory In ath12k_dp_rx_msdu_coalesce(), rxcb is fetched from skb and boolean is_continuation is part of rxcb. Currently, after freeing the skb, the rxcb->is_continuation accessed again which is wrong since the memory is already freed. This might lead use-after-free error. Hence, fix by locally defining bool is_continuation from rxcb, so that after freeing skb, is_continuation can be used. Compile tested only. Fixes: d889913205cf ("wifi: ath12k: driver for Qualcomm Wi-Fi 7 devices") Signed-off-by: Sarika Sharma Reviewed-by: Vasanthakumar Thiagarajan Link: https://patch.msgid.link/20250408045327.1632222-1-quic_sarishar@quicinc.com Signed-off-by: Jeff Johnson --- drivers/net/wireless/ath/ath12k/dp_rx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index 40feaa6d512d..3db1470109ca 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1841,6 +1841,7 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar, struct hal_rx_desc *ldesc; int space_extra, rem_len, buf_len; u32 hal_rx_desc_sz = ar->ab->hal.hal_desc_sz; + bool is_continuation; /* As the msdu is spread across multiple rx buffers, * find the offset to the start of msdu for computing @@ -1889,7 +1890,8 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar, rem_len = msdu_len - buf_first_len; while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) { rxcb = ATH12K_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; @@ -1907,7 +1909,7 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar, dev_kfree_skb_any(skb); rem_len -= buf_len; - if (!rxcb->is_continuation) + if (!is_continuation) break; } -- 2.51.0