>WARNING: This email originated from outside of Qualcomm. Please be wary of any >links or attachments, and do not enable macros. > >Add support to record the number of frames enqueued, hardware descriptor type, >encapsulation/encryption types used, frames dropped and completed. This is >useful for understanding the datapath performance and tune the peak throughput. > >The link specific stats can be viewed through the below debugfs file > >cat /sys/kernel/debug/ieee80211/phy0/netdev:wlan1/link_stats >link[0] Tx Unicast Frames Enqueued = 9 >link[0] Tx Broadcast Frames Enqueued = 78689 link[0] Tx Frames Completed = >78698 link[0] Tx Frames Dropped = 0 link[0] Tx Frame descriptor Encap Type = >raw:0 native wifi:78698 ethernet:0 link[0] Tx Frame descriptor Encrypt Type = >0:78698 1:0 2:0 3:0 4:0 5:0 6:0 7:0 8:0 9:0 10:0 11:0 link[0] Tx Frame descriptor >Type = buffer:78698 extension:0 >------------------------------------------------------ >link[1] Tx Unicast Frames Enqueued = 0 >link[1] Tx Broadcast Frames Enqueued = 78689 link[1] Tx Frames Completed = >78689 link[1] Tx Frames Dropped = 0 link[1] Tx Frame descriptor Encap Type = >raw:0 native wifi:78689 ethernet:0 link[1] Tx Frame descriptor Encrypt Type = >0:78689 1:0 2:0 3:0 4:0 5:0 6:0 7:0 8:0 9:0 10:0 11:0 link[1] Tx Frame descriptor >Type = buffer:78689 extension:0 >------------------------------------------------------ > >Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-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: Balamurugan Mahalingam <quic_bmahalin@xxxxxxxxxxx> >--- > >Depends-on: [PATCH v4 0/2] wifi: ath12k: Add support for MLO Multicast Handling >Link: https://patchwork.kernel.org/project/linux-wireless/list/?series=930206 > >v4: > Rebased on top of dependent patch. > >v3: https://patchwork.kernel.org/project/linux- >wireless/patch/20250203080032.3983556-1-quic_bmahalin@xxxxxxxxxxx/ > Rebased on top of dependent patch. > >v2: https://patchwork.kernel.org/project/linux- >wireless/patch/20250203025714.3852826-1-quic_bmahalin@xxxxxxxxxxx/ > Rebased on top of master branch. > >v1: https://patchwork.kernel.org/project/linux- >wireless/patch/20250110205912.2585850-1-quic_bmahalin@xxxxxxxxxxx/ > > drivers/net/wireless/ath/ath12k/core.h | 2 + > drivers/net/wireless/ath/ath12k/debugfs.c | 118 +++++++++++++++++++++ > drivers/net/wireless/ath/ath12k/debugfs.h | 7 ++ > drivers/net/wireless/ath/ath12k/dp.h | 11 ++ > drivers/net/wireless/ath/ath12k/dp_tx.c | 51 ++++++++- > drivers/net/wireless/ath/ath12k/dp_tx.h | 3 +- > drivers/net/wireless/ath/ath12k/hal_desc.h | 6 +- > drivers/net/wireless/ath/ath12k/mac.c | 10 +- > 8 files changed, 202 insertions(+), 6 deletions(-) > >diff --git a/drivers/net/wireless/ath/ath12k/core.h >b/drivers/net/wireless/ath/ath12k/core.h >index b8c336bc02d8..118bf7ae914a 100644 >--- a/drivers/net/wireless/ath/ath12k/core.h >+++ b/drivers/net/wireless/ath/ath12k/core.h >@@ -301,6 +301,8 @@ struct ath12k_link_vif { > u8 link_id; > struct ath12k_vif *ahvif; > struct ath12k_rekey_data rekey_data; >+ struct ath12k_link_stats link_stats; >+ spinlock_t link_stats_lock; /* Protects updates to link_stats */ > }; > > struct ath12k_vif { >diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c >b/drivers/net/wireless/ath/ath12k/debugfs.c >index 6d6708486d14..d737a581bb85 100644 >--- a/drivers/net/wireless/ath/ath12k/debugfs.c >+++ b/drivers/net/wireless/ath/ath12k/debugfs.c >@@ -32,6 +32,124 @@ static const struct file_operations fops_simulate_radar = { > .open = simple_open > }; > >+static int ath12k_open_link_stats(struct inode *inode, struct file >+*file) { >+ struct ath12k_vif *ahvif = inode->i_private; >+ size_t len = 0, buf_len = (PAGE_SIZE * 2); >+ struct ath12k_link_stats linkstat; >+ struct ath12k_link_vif *arvif; >+ unsigned long links_map; >+ struct wiphy *wiphy; >+ int link_id, i; >+ char *buf; >+ >+ if (!ahvif) >+ return -EINVAL; >+ >+ buf = kzalloc(buf_len, GFP_KERNEL); >+ if (!buf) >+ return -ENOMEM; >+ >+ wiphy = ahvif->ah->hw->wiphy; >+ wiphy_lock(wiphy); >+ >+ links_map = ahvif->links_map; >+ for_each_set_bit(link_id, &links_map, >+ IEEE80211_MLD_MAX_NUM_LINKS) { >+ arvif = rcu_dereference_protected(ahvif->link[link_id], >+ >+ lockdep_is_held(&wiphy->mtx)); >+ Here arvif can be NULL, so it would be good to check before using it. >+ spin_lock_bh(&arvif->link_stats_lock); >+ linkstat = arvif->link_stats; >+ spin_unlock_bh(&arvif->link_stats_lock); >+ >+ len += scnprintf(buf + len, buf_len - len, >+ "link[%d] Tx Unicast Frames Enqueued = %d\n", >+ link_id, linkstat.tx_enqueued); >+ len += scnprintf(buf + len, buf_len - len, >+ "link[%d] Tx Broadcast Frames Enqueued = %d\n", >+ link_id, linkstat.tx_bcast_mcast); >+ len += scnprintf(buf + len, buf_len - len, >+ "link[%d] Tx Frames Completed = %d\n", >+ link_id, linkstat.tx_completed); >+ len += scnprintf(buf + len, buf_len - len, >+ "link[%d] Tx Frames Dropped = %d\n", >+ link_id, linkstat.tx_dropped); >+ >+ len += scnprintf(buf + len, buf_len - len, >+ "link[%d] Tx Frame descriptor Encap Type = ", >+ link_id); >+ >+ len += scnprintf(buf + len, buf_len - len, >+ " raw:%d", >+ linkstat.tx_encap_type[0]); >+ >+ len += scnprintf(buf + len, buf_len - len, >+ " native_wifi:%d", >+ linkstat.tx_encap_type[1]); >+ >+ len += scnprintf(buf + len, buf_len - len, >+ " ethernet:%d", >+ linkstat.tx_encap_type[2]); Like encrypt type stats below this also can be put it in a loop. >+ >+ len += scnprintf(buf + len, buf_len - len, >+ "\nlink[%d] Tx Frame descriptor Encrypt Type = ", >+ link_id); >+ >+ for (i = 0; i < HAL_ENCRYPT_TYPE_MAX; i++) { >+ len += scnprintf(buf + len, buf_len - len, >+ " %d:%d", i, >+ linkstat.tx_encrypt_type[i]); >+ } >+ len += scnprintf(buf + len, buf_len - len, >+ "\nlink[%d] Tx Frame descriptor Type = buffer:%d >extension:%d\n", >+ link_id, linkstat.tx_desc_type[0], >+ linkstat.tx_desc_type[1]); >+ >+ len += scnprintf(buf + len, buf_len - len, >+ "------------------------------------------------------\n"); >+ } >+ >+ wiphy_unlock(wiphy); >+ >+ file->private_data = buf; >+ >+ return 0; >+} >+ >+static int ath12k_release_link_stats(struct inode *inode, struct file >+*file) { >+ kfree(file->private_data); >+ return 0; >+} >+ >+static ssize_t ath12k_read_link_stats(struct file *file, >+ char __user *user_buf, >+ size_t count, loff_t *ppos) { >+ const char *buf = file->private_data; >+ size_t len = strlen(buf); >+ >+ return simple_read_from_buffer(user_buf, count, ppos, buf, len); >+} >+ >+static const struct file_operations ath12k_fops_link_stats = { >+ .open = ath12k_open_link_stats, >+ .release = ath12k_release_link_stats, >+ .read = ath12k_read_link_stats, >+ .owner = THIS_MODULE, >+ .llseek = default_llseek, >+}; >+ >+void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, >+ struct ieee80211_vif *vif) { >+ struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); >+ >+ debugfs_create_file("link_stats", 0400, vif->debugfs_dir, ahvif, >+ &ath12k_fops_link_stats); } >+ > void ath12k_debugfs_soc_create(struct ath12k_base *ab) { > bool dput_needed; >diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h >b/drivers/net/wireless/ath/ath12k/debugfs.h >index 1c30745ee415..db87d9ad17c8 100644 >--- a/drivers/net/wireless/ath/ath12k/debugfs.h >+++ b/drivers/net/wireless/ath/ath12k/debugfs.h >@@ -15,6 +15,8 @@ void ath12k_debugfs_unregister(struct ath12k *ar); void >ath12k_debugfs_fw_stats_process(struct ath12k *ar, > struct ath12k_fw_stats *stats); void >ath12k_debugfs_fw_stats_reset(struct ath12k *ar); >+void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, >+ struct ieee80211_vif *vif); > #else > static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab) { @@ -40,6 >+42,11 @@ static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar, >static inline void ath12k_debugfs_fw_stats_reset(struct ath12k *ar) { } >+ >+static inline void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, >+ struct ieee80211_vif *vif) >+{ } > #endif /* CONFIG_ATH12K_DEBUGFS */ > > #endif /* _ATH12K_DEBUGFS_H_ */ >diff --git a/drivers/net/wireless/ath/ath12k/dp.h >b/drivers/net/wireless/ath/ath12k/dp.h >index 75435a931548..1bcac114f09a 100644 >--- a/drivers/net/wireless/ath/ath12k/dp.h >+++ b/drivers/net/wireless/ath/ath12k/dp.h >@@ -7,6 +7,7 @@ > #ifndef ATH12K_DP_H > #define ATH12K_DP_H > >+#include "hal_desc.h" > #include "hal_rx.h" > #include "hw.h" > >@@ -313,6 +314,16 @@ struct ath12k_reo_q_addr_lut { > u32 *vaddr; > }; > >+struct ath12k_link_stats { >+ u32 tx_enqueued; >+ u32 tx_completed; >+ u32 tx_bcast_mcast; >+ u32 tx_dropped; >+ u32 tx_encap_type[HAL_TCL_ENCAP_TYPE_MAX]; >+ u32 tx_encrypt_type[HAL_ENCRYPT_TYPE_MAX]; >+ u32 tx_desc_type[HAL_TCL_DESC_TYPE_MAX]; >+}; >+ > struct ath12k_dp { > struct ath12k_base *ab; > u8 num_bank_profiles; >diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c >b/drivers/net/wireless/ath/ath12k/dp_tx.c >index 1896178d2d4f..94b9f41941a1 100644 >--- a/drivers/net/wireless/ath/ath12k/dp_tx.c >+++ b/drivers/net/wireless/ath/ath12k/dp_tx.c >@@ -219,7 +219,8 @@ static int ath12k_dp_tx_align_payload(struct ath12k_base >*ab, } > > int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, >- struct sk_buff *skb, bool gsn_valid, int mcbc_gsn) >+ struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, >+ bool is_mcast) > { > struct ath12k_base *ab = ar->ab; > struct ath12k_dp *dp = &ab->dp; >@@ -464,6 +465,17 @@ int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif >*arvif, > goto fail_unmap_dma; > } > >+ spin_lock_bh(&arvif->link_stats_lock); >+ arvif->link_stats.tx_encap_type[ti.encap_type]++; >+ arvif->link_stats.tx_encrypt_type[ti.encrypt_type]++; >+ arvif->link_stats.tx_desc_type[ti.type]++; >+ >+ if (is_mcast) >+ arvif->link_stats.tx_bcast_mcast++; >+ else >+ arvif->link_stats.tx_enqueued++; >+ spin_unlock_bh(&arvif->link_stats_lock); >+ > ath12k_hal_tx_cmd_desc_setup(ab, hal_tcl_desc, &ti); > > ath12k_hal_srng_access_end(ab, tcl_ring); @@ -487,6 +499,11 @@ int >ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, > > fail_remove_tx_buf: > ath12k_dp_tx_release_txbuf(dp, tx_desc, pool_id); >+ >+ spin_lock_bh(&arvif->link_stats_lock); >+ arvif->link_stats.tx_dropped++; >+ spin_unlock_bh(&arvif->link_stats_lock); >+ > if (tcl_ring_retry) > goto tcl_ring_sel; > >@@ -522,7 +539,10 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base >*ab, > struct ath12k_dp_htt_wbm_tx_status *ts) { > struct ieee80211_tx_info *info; >+ struct ath12k_link_vif *arvif; > struct ath12k_skb_cb *skb_cb; >+ struct ieee80211_vif *vif; >+ struct ath12k_vif *ahvif; > struct ath12k *ar; > > skb_cb = ATH12K_SKB_CB(msdu); >@@ -538,6 +558,19 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base >*ab, > dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, > sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); > >+ vif = skb_cb->vif; >+ if (vif) { >+ ahvif = ath12k_vif_to_ahvif(vif); >+ rcu_read_lock(); >+ arvif = rcu_dereference(ahvif->link[skb_cb->link_id]); >+ if (arvif) { >+ spin_lock_bh(&arvif->link_stats_lock); >+ arvif->link_stats.tx_completed++; >+ spin_unlock_bh(&arvif->link_stats_lock); >+ } >+ rcu_read_unlock(); >+ } >+ > memset(&info->status, 0, sizeof(info->status)); > > if (ts->acked) { >@@ -592,7 +625,7 @@ ath12k_dp_tx_process_htt_tx_complete(struct >ath12k_base *ab, > */ > break; > default: >- ath12k_warn(ab, "Unknown htt tx status %d\n", wbm_status); >+ ath12k_warn(ab, "Unknown htt wbm tx status %d\n", >+ wbm_status); > break; > } > } >@@ -722,7 +755,10 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k >*ar, > struct ath12k_base *ab = ar->ab; > struct ath12k_hw *ah = ar->ah; > struct ieee80211_tx_info *info; >+ struct ath12k_link_vif *arvif; > struct ath12k_skb_cb *skb_cb; >+ struct ieee80211_vif *vif; >+ struct ath12k_vif *ahvif; > > if (WARN_ON_ONCE(ts->buf_rel_source != >HAL_WBM_REL_SRC_MODULE_TQM)) { > /* Must not happen */ >@@ -748,6 +784,17 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k >*ar, > goto exit; > } > >+ vif = skb_cb->vif; >+ if (vif) { >+ ahvif = ath12k_vif_to_ahvif(vif); >+ arvif = rcu_dereference(ahvif->link[skb_cb->link_id]); >+ if (arvif) { >+ spin_lock_bh(&arvif->link_stats_lock); >+ arvif->link_stats.tx_completed++; >+ spin_unlock_bh(&arvif->link_stats_lock); >+ } >+ } >+ > info = IEEE80211_SKB_CB(msdu); > memset(&info->status, 0, sizeof(info->status)); > >diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.h >b/drivers/net/wireless/ath/ath12k/dp_tx.h >index a5904097dc34..10acdcf1fa8f 100644 >--- a/drivers/net/wireless/ath/ath12k/dp_tx.h >+++ b/drivers/net/wireless/ath/ath12k/dp_tx.h >@@ -17,7 +17,8 @@ struct ath12k_dp_htt_wbm_tx_status { > > int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab); int >ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, >- struct sk_buff *skb, bool gsn_valid, int mcbc_gsn); >+ struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, >+ bool is_mcast); > void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id); > > int ath12k_dp_tx_htt_h2t_ppdu_stats_req(struct ath12k *ar, u32 mask); diff --git >a/drivers/net/wireless/ath/ath12k/hal_desc.h >b/drivers/net/wireless/ath/ath12k/hal_desc.h >index 3e8983b85de8..aba1023ec619 100644 >--- a/drivers/net/wireless/ath/ath12k/hal_desc.h >+++ b/drivers/net/wireless/ath/ath12k/hal_desc.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-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" > >@@ -1263,6 +1263,7 @@ struct hal_reo_flush_cache { > > #define HAL_TCL_DATA_CMD_INFO5_RING_ID GENMASK(27, 20) > #define HAL_TCL_DATA_CMD_INFO5_LOOPING_COUNT GENMASK(31, 28) >+#define HAL_ENCRYPT_TYPE_MAX 12 > > enum hal_encrypt_type { > HAL_ENCRYPT_TYPE_WEP_40, >@@ -1284,11 +1285,13 @@ enum hal_tcl_encap_type { > HAL_TCL_ENCAP_TYPE_NATIVE_WIFI, > HAL_TCL_ENCAP_TYPE_ETHERNET, > HAL_TCL_ENCAP_TYPE_802_3 = 3, >+ HAL_TCL_ENCAP_TYPE_MAX > }; > > enum hal_tcl_desc_type { > HAL_TCL_DESC_TYPE_BUFFER, > HAL_TCL_DESC_TYPE_EXT_DESC, >+ HAL_TCL_DESC_TYPE_MAX, > }; > > enum hal_wbm_htt_tx_comp_status { >@@ -1298,6 +1301,7 @@ enum hal_wbm_htt_tx_comp_status { > HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ, > HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT, > HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY, >+ HAL_WBM_REL_HTT_TX_COMP_STATUS_VDEVID_MISMATCH, > HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX, > }; > >diff --git a/drivers/net/wireless/ath/ath12k/mac.c >b/drivers/net/wireless/ath/ath12k/mac.c >index b3907ac05f69..95c8c30df4c0 100644 >--- a/drivers/net/wireless/ath/ath12k/mac.c >+++ b/drivers/net/wireless/ath/ath12k/mac.c >@@ -3980,6 +3980,9 @@ static struct ath12k_link_vif >*ath12k_mac_assign_link_vif(struct ath12k_hw *ah, > arvif->link_id = link_id; > ahvif->links_map |= BIT(link_id); > >+ /* Protects the datapath stats update on a per link basis */ >+ spin_lock_init(&arvif->link_stats_lock); >+ > INIT_LIST_HEAD(&arvif->list); > INIT_DELAYED_WORK(&arvif->connection_loss_work, > ath12k_mac_vif_sta_connection_loss_work); >@@ -7347,7 +7350,7 @@ static void ath12k_mac_op_tx(struct ieee80211_hw >*hw, > > if (!vif->valid_links || !is_mcast || > test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) { >- ret = ath12k_dp_tx(ar, arvif, skb, false, 0); >+ ret = ath12k_dp_tx(ar, arvif, skb, false, 0, is_mcast); > if (unlikely(ret)) { > ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); > ieee80211_free_txskb(ar->ah->hw, skb); @@ -7411,7 +7414,7 @@ >static void ath12k_mac_op_tx(struct ieee80211_hw *hw, > > skip_peer_find: > ret = ath12k_dp_tx(tmp_ar, tmp_arvif, >- msdu_copied, true, mcbc_gsn); >+ msdu_copied, true, mcbc_gsn, >+ is_mcast); > if (unlikely(ret)) { > if (ret == -ENOMEM) { > /* Drops are expected during heavy multicast @@ -10556,6 >+10559,9 @@ static const struct ieee80211_ops ath12k_ops = { > .suspend = ath12k_wow_op_suspend, > .resume = ath12k_wow_op_resume, > .set_wakeup = ath12k_wow_op_set_wakeup, >+#endif >+#ifdef CONFIG_ATH12K_DEBUGFS >+ .vif_add_debugfs = ath12k_debugfs_op_vif_add, > #endif > CFG80211_TESTMODE_CMD(ath12k_tm_cmd) > }; >-- >2.34.1 > - Tamizh