--- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -197,6 +197,16 @@ enum ath11k_dev_flags { ATH11K_FLAG_FW_RESTART_FOR_HOST, }; +#define ATH11K_STATS_MGMT_FRM_TYPE_MAX 16 + +struct ath11k_mgmt_frame_stats { + u32 tx_succ_cnt[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; + u32 tx_fail_cnt[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; + u32 rx_cnt[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; + u32 tx_compl_succ[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; + u32 tx_compl_fail[ATH11K_STATS_MGMT_FRM_TYPE_MAX]; +}; + struct ath11k_vif { u32 vdev_id; enum wmi_vdev_type vdev_type; @@ -241,6 +251,7 @@ struct ath11k_vif { int rtscts_prot_mode; int txpower; struct dentry *debugfs_twt; + struct ath11k_mgmt_frame_stats mgmt_stats; }; struct ath11k_vif_iter { --- a/drivers/net/wireless/ath/ath11k/debug.c +++ b/drivers/net/wireless/ath/ath11k/debug.c @@ -1626,6 +1626,87 @@ static const struct file_operations fops .open = simple_open }; +static ssize_t ath11k_dump_mgmt_stats(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath11k *ar = file->private_data; + struct ath11k_vif *arvif = NULL; + struct ath11k_mgmt_frame_stats *mgmt_stats; + int len = 0, ret, i; + int size = (TARGET_NUM_VDEVS - 1) * 1500; + char *buf; + const char *mgmt_frm_type[ATH11K_STATS_MGMT_FRM_TYPE_MAX-1] = {"assoc_req", "assoc_resp", + "reassoc_req", "reassoc_resp", + "probe_req", "probe_resp", + "timing_advertisement", "reserved", + "beacon", "atim", "disassoc", + "auth", "deauth", "action", "action_no_ack"}; + + if (ar->state != ATH11K_STATE_ON) + return -ENETDOWN; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + spin_lock_bh(&ar->data_lock); + + list_for_each_entry (arvif, &ar->arvifs, list) { + if (!arvif) + break; + + if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) + continue; + + mgmt_stats = &arvif->mgmt_stats; + len += scnprintf(buf + len, size - len, "MGMT frame stats for vdev %u :\n", + arvif->vdev_id); + len += scnprintf(buf + len, size - len, " TX stats :\n "); + len += scnprintf(buf + len, size - len, " Success frames:\n"); + for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) + len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], + mgmt_stats->tx_succ_cnt[i]); + + len += scnprintf(buf + len, size - len, " Failed frames:\n"); + + for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) + len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], + mgmt_stats->tx_fail_cnt[i]); + + len += scnprintf(buf + len, size - len, " RX stats :\n"); + len += scnprintf(buf + len, size - len, " Success frames:\n"); + for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) + len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], + mgmt_stats->rx_cnt[i]); + + len += scnprintf(buf + len, size - len, " Tx completion stats :\n"); + len += scnprintf(buf + len, size - len, " success completions:\n"); + for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) + len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], + mgmt_stats->tx_compl_succ[i]); + len += scnprintf(buf + len, size - len, " failure completions:\n"); + for (i = 0; i < ATH11K_STATS_MGMT_FRM_TYPE_MAX-1; i++) + len += scnprintf(buf + len, size - len, " %s: %d\n", mgmt_frm_type[i], + mgmt_stats->tx_compl_fail[i]); + } + + spin_unlock_bh(&ar->data_lock); + + if (len > size) + len = size; + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, len); + mutex_unlock(&ar->conf_mutex); + kfree(buf); + return ret; +} + +static const struct file_operations fops_dump_mgmt_stats = { + .read = ath11k_dump_mgmt_stats, + .open = simple_open +}; + int ath11k_debug_register(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; @@ -1670,6 +1751,8 @@ int ath11k_debug_register(struct ath11k debugfs_create_file("btcoex_duty_cycle", 0644, ar->debug.debugfs_pdev, ar, &fops__btcoex_duty_cycle); + debugfs_create_file("dump_mgmt_stats", 0644, ar->debug.debugfs_pdev, + ar, &fops_dump_mgmt_stats); if (ar->hw->wiphy->bands[NL80211_BAND_5GHZ]) { debugfs_create_file("dfs_simulate_radar", 0200, --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4679,21 +4679,36 @@ static void ath11k_mac_op_tx(struct ieee struct ieee80211_vif *vif = info->control.vif; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath11k_mgmt_frame_stats *mgmt_stats = &arvif->mgmt_stats; bool is_prb_rsp; int ret; + u16 frm_type = 0; skb_cb->flags = 0; if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) { skb_cb->flags = ATH11K_SKB_HW_80211_ENCAP; } else if (ieee80211_is_mgmt(hdr->frame_control)) { + frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control); is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control); ret = ath11k_mac_mgmt_tx(ar, skb, is_prb_rsp); if (ret) { - if (ret != -EBUSY) { + if (ret != -EBUSY) ath11k_warn(ar->ab, "failed to queue management frame %d\n", ret); - } ieee80211_free_txskb(ar->hw, skb); + spin_lock_bh(&ar->data_lock); + + mgmt_stats->tx_fail_cnt[frm_type]++; + + spin_unlock_bh(&ar->data_lock); + + } else { + + spin_lock_bh(&ar->data_lock); + + mgmt_stats->tx_succ_cnt[frm_type]++; + + spin_unlock_bh(&ar->data_lock); } return; } --- a/drivers/net/wireless/ath/ath11k/peer.c +++ b/drivers/net/wireless/ath/ath11k/peer.c @@ -335,6 +335,7 @@ int ath11k_peer_create(struct ath11k *ar peer->sec_type = HAL_ENCRYPT_TYPE_OPEN; peer->sec_type_grp = HAL_ENCRYPT_TYPE_OPEN; + peer->vif = arvif->vif; ar->num_peers++; --- a/drivers/net/wireless/ath/ath11k/peer.h +++ b/drivers/net/wireless/ath/ath11k/peer.h @@ -20,6 +20,7 @@ struct ppdu_user_delayba { struct ath11k_peer { struct list_head list; struct ieee80211_sta *sta; + struct ieee80211_vif *vif; int vdev_id; u8 addr[ETH_ALEN]; int peer_id; --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -4705,6 +4705,12 @@ static int wmi_process_mgmt_tx_comp(stru struct sk_buff *msdu; struct ieee80211_tx_info *info; struct ath11k_skb_cb *skb_cb; + struct ieee80211_hdr *hdr; + struct ath11k_peer *peer; + struct ieee80211_vif *vif; + struct ath11k_vif *arvif; + struct ath11k_mgmt_frame_stats *mgmt_stats; + u16 frm_type; spin_lock_bh(&ar->txmgmt_idr_lock); msdu = idr_find(&ar->txmgmt_idr, desc_id); @@ -4726,6 +4732,31 @@ static int wmi_process_mgmt_tx_comp(stru if ((!(info->flags & IEEE80211_TX_CTL_NO_ACK)) && !status) info->flags |= IEEE80211_TX_STAT_ACK; + hdr = (struct ieee80211_hdr *)msdu->data; + frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, hdr->frame_control); + + spin_lock_bh(&ar->ab->base_lock); + peer = ath11k_peer_find_by_addr(ar->ab, hdr->addr2); + if (!peer) { + spin_unlock_bh(&ar->ab->base_lock); + ath11k_warn(ar->ab, "failed to find peer to update txcompl mgmt stats\n"); + goto skip_mgmt_stats; + } + + vif = peer->vif; + spin_unlock_bh(&ar->ab->base_lock); + + spin_lock_bh(&ar->data_lock); + arvif = ath11k_vif_to_arvif(vif); + mgmt_stats = &arvif->mgmt_stats; + + if (!status) + mgmt_stats->tx_compl_succ[frm_type]++; + else + mgmt_stats->tx_compl_fail[frm_type]++; + spin_unlock_bh(&ar->data_lock); + +skip_mgmt_stats: ieee80211_tx_status_irqsafe(ar->hw, msdu); /* WARN when we received this event without doing any mgmt tx */ @@ -6049,6 +6080,11 @@ static void ath11k_mgmt_rx_event(struct struct ieee80211_hdr *hdr; u16 fc; struct ieee80211_supported_band *sband; + struct ath11k_peer *peer; + struct ieee80211_vif *vif; + struct ath11k_vif *arvif; + struct ath11k_mgmt_frame_stats *mgmt_stats; + u16 frm_type = 0; if (ath11k_pull_mgmt_rx_params_tlv(ab, skb, &rx_ev) != 0) { ath11k_warn(ab, "failed to extract mgmt rx event"); @@ -6107,7 +6143,30 @@ static void ath11k_mgmt_rx_event(struct hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); + frm_type = FIELD_GET(IEEE80211_FCTL_STYPE, fc); + + spin_lock_bh(&ab->base_lock); + + peer = ath11k_peer_find_by_addr(ab, hdr->addr1); + if(!peer) + peer = ath11k_peer_find_by_addr(ab, hdr->addr3); + if (!peer) { + spin_unlock_bh(&ab->base_lock); + goto skip_mgmt_stats; + } + + vif = peer->vif; + + spin_unlock_bh(&ab->base_lock); + spin_lock_bh(&ar->data_lock); + + arvif = ath11k_vif_to_arvif(vif); + mgmt_stats = &arvif->mgmt_stats; + mgmt_stats->rx_cnt[frm_type]++; + + spin_unlock_bh(&ar->data_lock); +skip_mgmt_stats: /* Firmware is guaranteed to report all essential management frames via * WMI while it can deliver some extra via HTT. Since there can be * duplicates split the reporting wrt monitor/sniffing.