diff options
-rw-r--r-- | drivers/net/wireless/ath/ath10k/core.h | 45 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/debug.c | 47 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/debug.h | 31 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/debugfs_sta.c | 286 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath10k/htt_rx.c | 71 |
5 files changed, 470 insertions, 10 deletions
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index fe6b30356d3b..c624b96f8b84 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -354,6 +355,45 @@ struct ath10k_txq { unsigned long num_push_allowed; }; +enum ath10k_pkt_rx_err { + ATH10K_PKT_RX_ERR_FCS, + ATH10K_PKT_RX_ERR_TKIP, + ATH10K_PKT_RX_ERR_CRYPT, + ATH10K_PKT_RX_ERR_PEER_IDX_INVAL, + ATH10K_PKT_RX_ERR_MAX, +}; + +enum ath10k_ampdu_subfrm_num { + ATH10K_AMPDU_SUBFRM_NUM_10, + ATH10K_AMPDU_SUBFRM_NUM_20, + ATH10K_AMPDU_SUBFRM_NUM_30, + ATH10K_AMPDU_SUBFRM_NUM_40, + ATH10K_AMPDU_SUBFRM_NUM_50, + ATH10K_AMPDU_SUBFRM_NUM_60, + ATH10K_AMPDU_SUBFRM_NUM_MORE, + ATH10K_AMPDU_SUBFRM_NUM_MAX, +}; + +enum ath10k_amsdu_subfrm_num { + ATH10K_AMSDU_SUBFRM_NUM_1, + ATH10K_AMSDU_SUBFRM_NUM_2, + ATH10K_AMSDU_SUBFRM_NUM_3, + ATH10K_AMSDU_SUBFRM_NUM_4, + ATH10K_AMSDU_SUBFRM_NUM_MORE, + ATH10K_AMSDU_SUBFRM_NUM_MAX, +}; + +struct ath10k_sta_tid_stats { + unsigned long int rx_pkt_from_fw; + unsigned long int rx_pkt_unchained; + unsigned long int rx_pkt_drop_chained; + unsigned long int rx_pkt_drop_filter; + unsigned long int rx_pkt_err[ATH10K_PKT_RX_ERR_MAX]; + unsigned long int rx_pkt_queued_for_mac; + unsigned long int rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MAX]; + unsigned long int rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MAX]; +}; + struct ath10k_sta { struct ath10k_vif *arvif; @@ -371,6 +411,9 @@ struct ath10k_sta { #ifdef CONFIG_MAC80211_DEBUGFS /* protected by conf_mutex */ bool aggr_mode; + + /* Protected with ar->data_lock */ + struct ath10k_sta_tid_stats tid_stats[IEEE80211_NUM_TIDS + 1]; #endif }; @@ -1019,6 +1062,8 @@ struct ath10k { void *ce_priv; + u32 sta_tid_stats_mask; + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 554cd7856cb6..1b9c092d210f 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -2143,6 +2143,48 @@ static const struct file_operations fops_fw_checksums = { .llseek = default_llseek, }; +static ssize_t ath10k_sta_tid_stats_mask_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + size_t len; + + len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->sta_tid_stats_mask); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_sta_tid_stats_mask_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + ssize_t len; + u32 mask; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoint(buf, 0, &mask)) + return -EINVAL; + + ar->sta_tid_stats_mask = mask; + + return len; +} + +static const struct file_operations fops_sta_tid_stats_mask = { + .read = ath10k_sta_tid_stats_mask_read, + .write = ath10k_sta_tid_stats_mask_write, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_create(struct ath10k *ar) { ar->debug.cal_data = vzalloc(ATH10K_DEBUG_CAL_DATA_LEN); @@ -2258,6 +2300,11 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("fw_checksums", 0400, ar->debug.debugfs_phy, ar, &fops_fw_checksums); + if (IS_ENABLED(CONFIG_MAC80211_DEBUGFS)) + debugfs_create_file("sta_tid_stats_mask", 0600, + ar->debug.debugfs_phy, + ar, &fops_sta_tid_stats_mask); + return 0; } diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index e54308889e59..7ebb9b1e7e69 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -191,12 +192,42 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); void ath10k_sta_update_rx_duration(struct ath10k *ar, struct ath10k_fw_stats *stats); +void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr, + unsigned long int num_msdus, + enum ath10k_pkt_rx_err err, + unsigned long int unchain_cnt, + unsigned long int drop_cnt, + unsigned long int drop_cnt_filter, + unsigned long int queued_msdus); +void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, + u16 peer_id, u8 tid, + struct htt_rx_indication_mpdu_range *ranges, + int num_ranges); #else static inline void ath10k_sta_update_rx_duration(struct ath10k *ar, struct ath10k_fw_stats *stats) { } + +static inline +void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr, + unsigned long int num_msdus, + enum ath10k_pkt_rx_err err, + unsigned long int unchain_cnt, + unsigned long int drop_cnt, + unsigned long int drop_cnt_filter, + unsigned long int queued_msdus) +{ +} + +static inline +void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, + u16 peer_id, u8 tid, + struct htt_rx_indication_mpdu_range *ranges, + int num_ranges) +{ +} #endif /* CONFIG_MAC80211_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c index b260b09dd4d3..8f688f136c22 100644 --- a/drivers/net/wireless/ath/ath10k/debugfs_sta.c +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -16,8 +17,125 @@ #include "core.h" #include "wmi-ops.h" +#include "txrx.h" #include "debug.h" +static void ath10k_rx_stats_update_amsdu_subfrm(struct ath10k *ar, + struct ath10k_sta_tid_stats *stats, + u32 msdu_count) +{ + if (msdu_count == 1) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_1]++; + else if (msdu_count == 2) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_2]++; + else if (msdu_count == 3) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_3]++; + else if (msdu_count == 4) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_4]++; + else if (msdu_count > 4) + stats->rx_pkt_amsdu[ATH10K_AMSDU_SUBFRM_NUM_MORE]++; +} + +static void ath10k_rx_stats_update_ampdu_subfrm(struct ath10k *ar, + struct ath10k_sta_tid_stats *stats, + u32 mpdu_count) +{ + if (mpdu_count <= 10) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_10]++; + else if (mpdu_count <= 20) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_20]++; + else if (mpdu_count <= 30) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_30]++; + else if (mpdu_count <= 40) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_40]++; + else if (mpdu_count <= 50) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_50]++; + else if (mpdu_count <= 60) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_60]++; + else if (mpdu_count > 60) + stats->rx_pkt_ampdu[ATH10K_AMPDU_SUBFRM_NUM_MORE]++; +} + +void ath10k_sta_update_rx_tid_stats_ampdu(struct ath10k *ar, u16 peer_id, u8 tid, + struct htt_rx_indication_mpdu_range *ranges, + int num_ranges) +{ + struct ath10k_sta *arsta; + struct ath10k_peer *peer; + int i; + + if (tid > IEEE80211_NUM_TIDS || !(ar->sta_tid_stats_mask & BIT(tid))) + return; + + rcu_read_lock(); + spin_lock_bh(&ar->data_lock); + + peer = ath10k_peer_find_by_id(ar, peer_id); + if (!peer) + goto out; + + arsta = (struct ath10k_sta *)peer->sta->drv_priv; + + for (i = 0; i < num_ranges; i++) + ath10k_rx_stats_update_ampdu_subfrm(ar, + &arsta->tid_stats[tid], + ranges[i].mpdu_count); + +out: + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); +} + +void ath10k_sta_update_rx_tid_stats(struct ath10k *ar, u8 *first_hdr, + unsigned long int num_msdus, + enum ath10k_pkt_rx_err err, + unsigned long int unchain_cnt, + unsigned long int drop_cnt, + unsigned long int drop_cnt_filter, + unsigned long int queued_msdus) +{ + struct ieee80211_sta *sta; + struct ath10k_sta *arsta; + struct ieee80211_hdr *hdr; + struct ath10k_sta_tid_stats *stats; + u8 tid = IEEE80211_NUM_TIDS; + bool non_data_frm = false; + + hdr = (struct ieee80211_hdr *)first_hdr; + if (!ieee80211_is_data(hdr->frame_control)) + non_data_frm = true; + + if (ieee80211_is_data_qos(hdr->frame_control)) + tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; + + if (!(ar->sta_tid_stats_mask & BIT(tid)) || non_data_frm) + return; + + rcu_read_lock(); + + sta = ieee80211_find_sta_by_ifaddr(ar->hw, hdr->addr2, NULL); + if (!sta) + goto exit; + + arsta = (struct ath10k_sta *)sta->drv_priv; + + spin_lock_bh(&ar->data_lock); + stats = &arsta->tid_stats[tid]; + stats->rx_pkt_from_fw += num_msdus; + stats->rx_pkt_unchained += unchain_cnt; + stats->rx_pkt_drop_chained += drop_cnt; + stats->rx_pkt_drop_filter += drop_cnt_filter; + if (err != ATH10K_PKT_RX_ERR_MAX) + stats->rx_pkt_err[err] += queued_msdus; + stats->rx_pkt_queued_for_mac += queued_msdus; + ath10k_rx_stats_update_amsdu_subfrm(ar, &arsta->tid_stats[tid], + num_msdus); + spin_unlock_bh(&ar->data_lock); + +exit: + rcu_read_unlock(); +} + static void ath10k_sta_update_extd_stats_rx_duration(struct ath10k *ar, struct ath10k_fw_stats *stats) { @@ -342,6 +460,172 @@ static const struct file_operations fops_peer_debug_trigger = { .llseek = default_llseek, }; +static char *get_err_str(enum ath10k_pkt_rx_err i) +{ + switch (i) { + case ATH10K_PKT_RX_ERR_FCS: + return "fcs_err"; + case ATH10K_PKT_RX_ERR_TKIP: + return "tkip_err"; + case ATH10K_PKT_RX_ERR_CRYPT: + return "crypt_err"; + case ATH10K_PKT_RX_ERR_PEER_IDX_INVAL: + return "peer_idx_inval"; + case ATH10K_PKT_RX_ERR_MAX: + return "unknown"; + } + + return "unknown"; +} + +static char *get_num_ampdu_subfrm_str(enum ath10k_ampdu_subfrm_num i) +{ + switch (i) { + case ATH10K_AMPDU_SUBFRM_NUM_10: + return "upto 10"; + case ATH10K_AMPDU_SUBFRM_NUM_20: + return "11-20"; + case ATH10K_AMPDU_SUBFRM_NUM_30: + return "21-30"; + case ATH10K_AMPDU_SUBFRM_NUM_40: + return "31-40"; + case ATH10K_AMPDU_SUBFRM_NUM_50: + return "41-50"; + case ATH10K_AMPDU_SUBFRM_NUM_60: + return "51-60"; + case ATH10K_AMPDU_SUBFRM_NUM_MORE: + return ">60"; + case ATH10K_AMPDU_SUBFRM_NUM_MAX: + return "0"; + } + + return "0"; +} + +static char *get_num_amsdu_subfrm_str(enum ath10k_amsdu_subfrm_num i) +{ + switch (i) { + case ATH10K_AMSDU_SUBFRM_NUM_1: + return "1"; + case ATH10K_AMSDU_SUBFRM_NUM_2: + return "2"; + case ATH10K_AMSDU_SUBFRM_NUM_3: + return "3"; + case ATH10K_AMSDU_SUBFRM_NUM_4: + return "4"; + case ATH10K_AMSDU_SUBFRM_NUM_MORE: + return ">4"; + case ATH10K_AMSDU_SUBFRM_NUM_MAX: + return "0"; + } + + return "0"; +} + +#define PRINT_TID_STATS(_field, _tabs) \ + do { \ + int k = 0; \ + for (j = 0; j <= IEEE80211_NUM_TIDS; j++) { \ + if (ar->sta_tid_stats_mask & BIT(j)) { \ + len += scnprintf(buf + len, buf_len - len, \ + "[%02d] %-10lu ", \ + j, stats[j]._field); \ + k++; \ + if (k % 8 == 0) { \ + len += scnprintf(buf + len, \ + buf_len - len, "\n"); \ + len += scnprintf(buf + len, \ + buf_len - len, \ + _tabs); \ + } \ + } \ + } \ + len += scnprintf(buf + len, buf_len - len, "\n"); \ + } while (0) + +static ssize_t ath10k_dbg_sta_read_tid_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arsta->arvif->ar; + struct ath10k_sta_tid_stats *stats = arsta->tid_stats; + size_t len = 0, buf_len = 1048 * IEEE80211_NUM_TIDS; + char *buf; + int i, j; + ssize_t ret; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&ar->conf_mutex); + + spin_lock_bh(&ar->data_lock); + + len += scnprintf(buf + len, buf_len - len, + "\n\t\tDriver Rx pkt stats per tid, ([tid] count)\n"); + len += scnprintf(buf + len, buf_len - len, + "\t\t------------------------------------------\n"); + len += scnprintf(buf + len, buf_len - len, "MSDUs from FW\t\t\t"); + PRINT_TID_STATS(rx_pkt_from_fw, "\t\t\t\t"); + + len += scnprintf(buf + len, buf_len - len, "MSDUs unchained\t\t\t"); + PRINT_TID_STATS(rx_pkt_unchained, "\t\t\t\t"); + + len += scnprintf(buf + len, buf_len - len, + "MSDUs locally dropped:chained\t"); + PRINT_TID_STATS(rx_pkt_drop_chained, "\t\t\t\t"); + + len += scnprintf(buf + len, buf_len - len, + "MSDUs locally dropped:filtered\t"); + PRINT_TID_STATS(rx_pkt_drop_filter, "\t\t\t\t"); + + len += scnprintf(buf + len, buf_len - len, + "MSDUs queued for mac80211\t"); + PRINT_TID_STATS(rx_pkt_queued_for_mac, "\t\t\t\t"); + + for (i = 0; i < ATH10K_PKT_RX_ERR_MAX; i++) { + len += scnprintf(buf + len, buf_len - len, + "MSDUs with error:%s\t", get_err_str(i)); + PRINT_TID_STATS(rx_pkt_err[i], "\t\t\t\t"); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + for (i = 0; i < ATH10K_AMPDU_SUBFRM_NUM_MAX; i++) { + len += scnprintf(buf + len, buf_len - len, + "A-MPDU num subframes %s\t", + get_num_ampdu_subfrm_str(i)); + PRINT_TID_STATS(rx_pkt_ampdu[i], "\t\t\t\t"); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + for (i = 0; i < ATH10K_AMSDU_SUBFRM_NUM_MAX; i++) { + len += scnprintf(buf + len, buf_len - len, + "A-MSDU num subframes %s\t\t", + get_num_amsdu_subfrm_str(i)); + PRINT_TID_STATS(rx_pkt_amsdu[i], "\t\t\t\t"); + } + + spin_unlock_bh(&ar->data_lock); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + kfree(buf); + + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static const struct file_operations fops_tid_stats_dump = { + .open = simple_open, + .read = ath10k_dbg_sta_read_tid_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir) { @@ -351,4 +635,6 @@ void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, debugfs_create_file("delba", 0200, dir, sta, &fops_delba); debugfs_create_file("peer_debug_trigger", 0600, dir, sta, &fops_peer_debug_trigger); + debugfs_create_file("dump_tid_stats", 0400, dir, sta, + &fops_tid_stats_dump); } diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 6d96f9560950..64996aba1485 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1502,7 +1503,9 @@ static void ath10k_htt_rx_h_csum_offload(struct sk_buff *msdu) static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, struct sk_buff_head *amsdu, struct ieee80211_rx_status *status, - bool fill_crypt_header) + bool fill_crypt_header, + u8 *rx_hdr, + enum ath10k_pkt_rx_err *err) { struct sk_buff *first; struct sk_buff *last; @@ -1538,6 +1541,9 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, hdr = (void *)rxd->rx_hdr_status; memcpy(first_hdr, hdr, RX_HTT_HDR_STATUS_LEN); + if (rx_hdr) + memcpy(rx_hdr, hdr, RX_HTT_HDR_STATUS_LEN); + /* Each A-MSDU subframe will use the original header as the base and be * reported as a separate MSDU so strip the A-MSDU bit from QoS Ctl. */ @@ -1581,6 +1587,17 @@ static void ath10k_htt_rx_h_mpdu(struct ath10k *ar, if (has_tkip_err) status->flag |= RX_FLAG_MMIC_ERROR; + if (err) { + if (has_fcs_err) + *err = ATH10K_PKT_RX_ERR_FCS; + else if (has_tkip_err) + *err = ATH10K_PKT_RX_ERR_TKIP; + else if (has_crypto_err) + *err = ATH10K_PKT_RX_ERR_CRYPT; + else if (has_peer_idx_invalid) + *err = ATH10K_PKT_RX_ERR_PEER_IDX_INVAL; + } + /* Firmware reports all necessary management frames via WMI already. * They are not reported to monitor interfaces at all so pass the ones * coming via HTT to monitor interfaces instead. This simplifies @@ -1651,11 +1668,13 @@ static void ath10k_htt_rx_h_enqueue(struct ath10k *ar, } } -static int ath10k_unchain_msdu(struct sk_buff_head *amsdu) +static int ath10k_unchain_msdu(struct sk_buff_head *amsdu, + unsigned long int *unchain_cnt) { struct sk_buff *skb, *first; int space; int total_len = 0; + int amsdu_len = skb_queue_len(amsdu); /* TODO: Might could optimize this by using * skb_try_coalesce or similar method to @@ -1691,11 +1710,16 @@ static int ath10k_unchain_msdu(struct sk_buff_head *amsdu) } __skb_queue_head(amsdu, first); + + *unchain_cnt += amsdu_len - 1; + return 0; } static void ath10k_htt_rx_h_unchain(struct ath10k *ar, - struct sk_buff_head *amsdu) + struct sk_buff_head *amsdu, + unsigned long int *drop_cnt, + unsigned long int *unchain_cnt) { struct sk_buff *first; struct htt_rx_desc *rxd; @@ -1713,11 +1737,12 @@ static void ath10k_htt_rx_h_unchain(struct ath10k *ar, */ if (decap != RX_MSDU_DECAP_RAW || skb_queue_len(amsdu) != 1 + rxd->frag_info.ring2_more_count) { + *drop_cnt += skb_queue_len(amsdu); __skb_queue_purge(amsdu); return; } - ath10k_unchain_msdu(amsdu); + ath10k_unchain_msdu(amsdu, unchain_cnt); } static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar, @@ -1743,7 +1768,8 @@ static bool ath10k_htt_rx_amsdu_allowed(struct ath10k *ar, static void ath10k_htt_rx_h_filter(struct ath10k *ar, struct sk_buff_head *amsdu, - struct ieee80211_rx_status *rx_status) + struct ieee80211_rx_status *rx_status, + unsigned long int *drop_cnt) { if (skb_queue_empty(amsdu)) return; @@ -1751,6 +1777,9 @@ static void ath10k_htt_rx_h_filter(struct ath10k *ar, if (ath10k_htt_rx_amsdu_allowed(ar, amsdu, rx_status)) return; + if (drop_cnt) + *drop_cnt += skb_queue_len(amsdu); + __skb_queue_purge(amsdu); } @@ -1760,6 +1789,12 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt) struct ieee80211_rx_status *rx_status = &htt->rx_status; struct sk_buff_head amsdu; int ret; + unsigned long int drop_cnt = 0; + unsigned long int unchain_cnt = 0; + unsigned long int drop_cnt_filter = 0; + unsigned long int msdus_to_queue, num_msdus; + enum ath10k_pkt_rx_err err = ATH10K_PKT_RX_ERR_MAX; + u8 first_hdr[RX_HTT_HDR_STATUS_LEN]; __skb_queue_head_init(&amsdu); @@ -1781,16 +1816,23 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt) return ret; } + num_msdus = skb_queue_len(&amsdu); + ath10k_htt_rx_h_ppdu(ar, &amsdu, rx_status, 0xffff); /* only for ret = 1 indicates chained msdus */ if (ret > 0) - ath10k_htt_rx_h_unchain(ar, &amsdu); + ath10k_htt_rx_h_unchain(ar, &amsdu, &drop_cnt, &unchain_cnt); - ath10k_htt_rx_h_filter(ar, &amsdu, rx_status); - ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true); + ath10k_htt_rx_h_filter(ar, &amsdu, rx_status, &drop_cnt_filter); + ath10k_htt_rx_h_mpdu(ar, &amsdu, rx_status, true, first_hdr, &err); + msdus_to_queue = skb_queue_len(&amsdu); ath10k_htt_rx_h_enqueue(ar, &amsdu, rx_status); + ath10k_sta_update_rx_tid_stats(ar, first_hdr, num_msdus, err, + unchain_cnt, drop_cnt, drop_cnt_filter, + msdus_to_queue); + return 0; } @@ -1801,9 +1843,14 @@ static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt, struct htt_rx_indication_mpdu_range *mpdu_ranges; int num_mpdu_ranges; int i, mpdu_count = 0; + u16 peer_id; + u8 tid; num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1), HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES); + peer_id = __le16_to_cpu(rx->hdr.peer_id); + tid = MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID); + mpdu_ranges = htt_rx_ind_get_mpdu_ranges(rx); ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx ind: ", @@ -1815,6 +1862,9 @@ static void ath10k_htt_rx_proc_rx_ind(struct ath10k_htt *htt, mpdu_count += mpdu_ranges[i].mpdu_count; atomic_add(mpdu_count, &htt->num_mpdus_ready); + + ath10k_sta_update_rx_tid_stats_ampdu(ar, peer_id, tid, mpdu_ranges, + num_mpdu_ranges); } static void ath10k_htt_rx_tx_compl_ind(struct ath10k *ar, @@ -2124,8 +2174,9 @@ static int ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) * should still give an idea about rx rate to the user. */ ath10k_htt_rx_h_ppdu(ar, &amsdu, status, vdev_id); - ath10k_htt_rx_h_filter(ar, &amsdu, status); - ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false); + ath10k_htt_rx_h_filter(ar, &amsdu, status, NULL); + ath10k_htt_rx_h_mpdu(ar, &amsdu, status, false, NULL, + NULL); ath10k_htt_rx_h_enqueue(ar, &amsdu, status); break; case -EAGAIN: |