From d6d86c0c48c8d114e94c5b5f749c97d629d727ef Mon Sep 17 00:00:00 2001
From: Maharaja Kennadyrajan <mkenna@codeaurora.org>
Date: Mon, 4 Jan 2021 23:49:21 +0530
Subject: [PATCH 1/2] ath11k/mac80211: Add support to account Tx and Rx flow
 packets

Added support to log the inflow and outflow of the Tx and Rx
packets in netif and host driver.

Command to dump the Tx pkts flow in driver:
cat
/sys/kernel/debug/ieee80211/phyX/netdev\:wlanX/stations/
XX\:XX\:XX\:XX\:XX\:XX/driver_tx_pkts_flow

Command to dump the Rx pkts flow in driver:
cat
/sys/kernel/debug/ieee80211/phyX/netdev\:wlanX/stations/
XX\:XX\:XX\:XX\:XX\:XX/driver_rx_pkts_flow

Commands to reset the Tx/Rx pkts flow in driver:
echo 1 >
/sys/kernel/debug/ieee80211/phyX/netdev\:wlanX/stations/
XX\:XX\:XX\:XX\:XX\:XX/reset_tx_stats

echo 1 >
/sys/kernel/debug/ieee80211/phyX/netdev\:wlanX/stations/
XX\:XX\:XX\:XX\:XX\:XX/reset_rx_stats

Command to dump the Tx pkts flow in mac80211:
cat
/sys/kernel/debug/ieee80211/phyX/netdev\:wlanX/stations/
XX\:XX\:XX\:XX\:XX\:XX/mac80211_tx_pkts_flow

Command to dump the Rx pkts flow in mac80211:
cat
/sys/kernel/debug/ieee80211/phyX/netdev\:wlanX/stations/
XX\:XX\:XX\:XX\:XX\:XX/mac80211_rx_pkts_flow

Commands to reset the Tx/Rx pkts flow in mac80211:
echo 1 >
/sys/kernel/debug/ieee80211/phyX/netdev\:wlanX/stations/
XX\:XX\:XX\:XX\:XX\:XX/reset_mac80211_tx_pkts_flow

echo 1 >
/sys/kernel/debug/ieee80211/phyX/netdev\:wlanX/stations/
XX\:XX\:XX\:XX\:XX\:XX/reset_mac80211_rx_pkts_flow

Sample output after running the Tx and Rx traffic.

root@OpenWrt:/# cat sys/kernel/debug/ieee80211/phy0/netdev\:
wlan0/stations/8c\:fd\:f0\:06\:23\:41/driver_tx_pkts_flow
Tx packets inflow from mac80211: 20
Tx packets outflow to HW: 20

root@OpenWrt:/# cat sys/kernel/debug/ieee80211/phy0/netdev\:
wlan0/stations/8c\:fd\:f0\:06\:23\:41/mac80211_tx_pkts_flow
Tx packets outflow from netif: 20
Tx packets inflow in mac80211: 20

root@OpenWrt:/# cat sys/kernel/debug/ieee80211/phy0/netdev\:
wlan0/stations/8c\:fd\:f0\:06\:23\:41/driver_rx_pkts_flow
Rx packets inflow from HW: 28
Rx packets outflow from driver: 28

root@OpenWrt:/# cat sys/kernel/debug/ieee80211/phy0/netdev\:
wlan0/stations/8c\:fd\:f0\:06\:23\:41/mac80211_rx_pkts_flow
Rx packets inflow in mac80211: 28
Rx packets inflow in netif: 26
Rx forwarded packets in bridge: 2

Signed-off-by: Maharaja Kennadyrajan <mkenna@codeaurora.org>
---
 drivers/net/wireless/ath/ath11k/core.h        |  12 ++
 drivers/net/wireless/ath/ath11k/debugfs.h       |   2 +
 drivers/net/wireless/ath/ath11k/debugfs_sta.c | 145 +++++++++++++++++-
 drivers/net/wireless/ath/ath11k/dp_rx.c       |  38 +++++
 drivers/net/wireless/ath/ath11k/mac.c         |  11 ++
 5 files changed, 207 insertions(+), 1 deletion(-)

--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -398,6 +398,16 @@ struct ath11k_per_ppdu_tx_stats {
 	u32 retry_bytes;
 };
 
+struct ath11k_driver_tx_pkts_flow {
+	atomic_t pkts_in;
+	atomic_t pkts_out;
+};
+
+struct ath11k_driver_rx_pkts_flow {
+	atomic_t pkts_frm_hw;
+	atomic_t pkts_out;
+};
+
 struct ath11k_sta {
 	struct ath11k_vif *arvif;
 
@@ -427,6 +437,8 @@ struct ath11k_sta {
 #ifdef CPTCFG_ATH11K_NSS_SUPPORT
 	struct ath11k_nss_sta_stats *nss_stats;
 #endif
+	struct ath11k_driver_tx_pkts_flow drv_tx_pkts;
+	struct ath11k_driver_rx_pkts_flow drv_rx_pkts;
 	u16 tcl_metadata;
 	/* Protected with ar->data_lock */
 	u32 peer_ps_state;
--- a/drivers/net/wireless/ath/ath11k/debugfs.h
+++ b/drivers/net/wireless/ath/ath11k/debugfs.h
@@ -72,6 +72,8 @@ enum ath11k_dbg_htt_ext_stats_type {
 #define TPC_STATS_TOT_COLUMN			100
 #define ATH11K_TPC_STATS_BUF_SIZE   (TPC_STATS_TOT_ROW * TPC_STATS_TOT_COLUMN)
 
+#define ATH11K_DRV_TX_STATS_SIZE      1024
+
 enum ath11k_debug_tpc_stats_type {
 	ATH11K_DBG_TPC_STATS_SU,
 	ATH11K_DBG_TPC_STATS_SU_WITH_TXBF,
--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c
@@ -1087,6 +1087,8 @@ static ssize_t ath11k_dbg_sta_reset_rx_s
 
 	spin_lock_bh(&ar->ab->base_lock);
 	memset(arsta->rx_stats, 0, sizeof(*arsta->rx_stats));
+	atomic_set(&arsta->drv_rx_pkts.pkts_frm_hw, 0);
+	atomic_set(&arsta->drv_rx_pkts.pkts_out, 0);
 	spin_unlock_bh(&ar->ab->base_lock);
 
 	ret = count;
@@ -1196,6 +1198,139 @@ static const struct file_operations fops
 	.llseek = default_llseek,
 };
 
+static ssize_t
+ath11k_dbg_sta_dump_driver_tx_pkts_flow(struct file *file,
+					const char __user *user_buf,
+					size_t count, loff_t *ppos)
+{
+	struct ieee80211_sta *sta = file->private_data;
+	struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
+	struct ath11k *ar = arsta->arvif->ar;
+	int len = 0, ret_val;
+	const int size = ATH11K_DRV_TX_STATS_SIZE;
+	char *buf;
+
+	if (!arsta->tx_stats)
+		return -ENOENT;
+
+	buf = kzalloc(ATH11K_DRV_TX_STATS_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&ar->conf_mutex);
+	spin_lock_bh(&ar->ab->base_lock);
+	len += scnprintf(buf + len, size - len,
+			 "Tx packets inflow from mac80211: %u\n",
+			 atomic_read(&arsta->drv_tx_pkts.pkts_in));
+	len += scnprintf(buf + len, size - len,
+			 "Tx packets outflow to HW: %u\n",
+			 atomic_read(&arsta->drv_tx_pkts.pkts_out));
+	spin_unlock_bh(&ar->ab->base_lock);
+
+	if (len > size)
+		len = size;
+
+	ret_val = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+
+	mutex_unlock(&ar->conf_mutex);
+	return ret_val;
+}
+
+static const struct file_operations fops_driver_tx_pkts_flow = {
+	.read = ath11k_dbg_sta_dump_driver_tx_pkts_flow,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t ath11k_dbg_sta_reset_tx_stats(struct file *file,
+					     const char __user *buf,
+					     size_t count, loff_t *ppos)
+{
+	struct ieee80211_sta *sta = file->private_data;
+	struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
+	struct ath11k *ar = arsta->arvif->ar;
+	int ret, reset;
+
+	if (!arsta->tx_stats)
+		return -ENOENT;
+
+	ret = kstrtoint_from_user(buf, count, 0, &reset);
+	if (ret)
+		return ret;
+
+	if (!reset || reset > 1)
+		return -EINVAL;
+
+	spin_lock_bh(&ar->ab->base_lock);
+	memset(arsta->tx_stats, 0, sizeof(*arsta->tx_stats));
+	atomic_set(&arsta->drv_tx_pkts.pkts_in, 0);
+	atomic_set(&arsta->drv_tx_pkts.pkts_out, 0);
+	spin_unlock_bh(&ar->ab->base_lock);
+
+	ret = count;
+	return ret;
+}
+
+static const struct file_operations fops_reset_tx_stats = {
+	.write = ath11k_dbg_sta_reset_tx_stats,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+static ssize_t
+ath11k_dbg_sta_dump_driver_rx_pkts_flow(struct file *file,
+					char __user *user_buf,
+					size_t count, loff_t *ppos)
+{
+	struct ieee80211_sta *sta = file->private_data;
+	struct ath11k_sta *arsta = (struct ath11k_sta *)sta->drv_priv;
+	struct ath11k *ar = arsta->arvif->ar;
+	struct ath11k_rx_peer_stats *rx_stats = arsta->rx_stats;
+	int len = 0, ret_val = 0;
+	const int size = 1024;
+	char *buf;
+
+	if (!rx_stats)
+		return -ENOENT;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	mutex_lock(&ar->conf_mutex);
+	spin_lock_bh(&ar->ab->base_lock);
+
+	len += scnprintf(buf + len, size - len,
+			 "Rx packets inflow from HW: %u\n",
+			 atomic_read(&arsta->drv_rx_pkts.pkts_frm_hw));
+	len += scnprintf(buf + len, size - len,
+			 "Rx packets outflow from driver: %u\n",
+			 atomic_read(&arsta->drv_rx_pkts.pkts_out));
+
+	len += scnprintf(buf + len, size - len, "\n");
+
+	spin_unlock_bh(&ar->ab->base_lock);
+
+	if (len > size)
+		len = size;
+
+	ret_val = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+
+	mutex_unlock(&ar->conf_mutex);
+	return ret_val;
+}
+
+static const struct file_operations fops_driver_rx_pkts_flow = {
+	.read = ath11k_dbg_sta_dump_driver_rx_pkts_flow,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 void ath11k_debugfs_sta_op_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 			       struct ieee80211_sta *sta, struct dentry *dir)
 {
@@ -1211,14 +1346,21 @@ void ath11k_debugfs_sta_op_add(struct ie
 		debugfs_create_file("total_ps_duration", 0440, dir, sta,
 				    &fops_total_ps_duration);
 	}
-	if (ath11k_debugfs_is_extd_tx_stats_enabled(ar))
+	if (ath11k_debugfs_is_extd_tx_stats_enabled(ar)) {
 		debugfs_create_file("tx_stats", 0400, dir, sta,
 				    &fops_tx_stats);
+		debugfs_create_file("reset_tx_stats", 0600, dir, sta,
+				    &fops_reset_tx_stats);
+		debugfs_create_file("driver_tx_pkts_flow", 0400, dir, sta,
+				    &fops_driver_tx_pkts_flow);
+	}
 	if (ath11k_debugfs_is_extd_rx_stats_enabled(ar)) {
 		debugfs_create_file("rx_stats", 0400, dir, sta,
 				    &fops_rx_stats);
 		debugfs_create_file("reset_rx_stats", 0600, dir, sta,
 				    &fops_reset_rx_stats);
+		debugfs_create_file("driver_rx_pkts_flow", 0400, dir, sta,
+				    &fops_driver_rx_pkts_flow);
 	}
 
 	debugfs_create_file("htt_peer_stats", 0400, dir, sta,
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -2692,6 +2692,7 @@ static void ath11k_dp_rx_deliver_msdu(st
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_radiotap_he *he = NULL;
 	struct ieee80211_sta *pubsta = NULL;
+	struct ath11k_sta *arsta = NULL;
 	struct ath11k_peer *peer;
 	struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
 	u8 decap = DP_RX_DECAP_TYPE_RAW;
@@ -2754,6 +2755,18 @@ static void ath11k_dp_rx_deliver_msdu(st
 		rx_status->flag |= RX_FLAG_8023;
 
 	ieee80211_rx_napi(ar->hw, pubsta, msdu, napi);
+
+	if (ath11k_debugfs_is_extd_rx_stats_enabled(ar)) {
+		if (!(status->flag & RX_FLAG_ONLY_MONITOR)) {
+			spin_lock_bh(&ar->ab->base_lock);
+			if (peer && peer->sta)
+				arsta =
+				(struct ath11k_sta *)peer->sta->drv_priv;
+			spin_unlock_bh(&ar->ab->base_lock);
+			if (arsta)
+				atomic_inc(&arsta->drv_rx_pkts.pkts_out);
+		}
+	}
 }
 
 static int ath11k_dp_rx_process_msdu(struct ath11k *ar,
@@ -2903,6 +2916,8 @@ int ath11k_dp_process_rx(struct ath11k_b
 	int total_msdu_reaped = 0;
 	struct hal_srng *srng;
 	struct sk_buff *msdu;
+	struct ath11k_peer *peer = NULL;
+	struct ath11k_sta *arsta = NULL;
 	int quota = budget;
 	bool done = false;
 	int buf_id, mac_id;
@@ -2974,6 +2989,19 @@ try_again:
 		rxcb->tid = FIELD_GET(HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM,
 				      desc.info0);
 
+		if (ath11k_debugfs_is_extd_rx_stats_enabled(ar) && rxcb->peer_id) {
+			rcu_read_lock();
+			spin_lock_bh(&ab->base_lock);
+			peer = ath11k_peer_find_by_id(ab, rxcb->peer_id);
+			if (peer && peer->sta)
+				arsta =
+				(struct ath11k_sta *)peer->sta->drv_priv;
+			spin_unlock_bh(&ab->base_lock);
+			if (arsta)
+				atomic_inc(&arsta->drv_rx_pkts.pkts_frm_hw);
+			rcu_read_unlock();
+		}
+
 		rxcb->mac_id = mac_id;
 		__skb_queue_tail(&msdu_list, msdu);
 
@@ -4458,7 +4486,10 @@ static int ath11k_dp_rx_h_null_q_desc(st
 	struct rx_attention *rx_attention;
 	u8 l3pad_bytes;
 	struct ath11k_skb_rxcb *rxcb = ATH11K_SKB_RXCB(msdu);
+	struct ath11k_peer *peer = NULL;
+	struct ath11k_sta *arsta = NULL;
 	u32 hal_rx_desc_sz = ar->ab->hw_params.hal_desc_sz;
+	u32 peer_id;
 
 	msdu_len = ath11k_dp_rx_h_msdu_start_msdu_len(ar->ab, desc);
 
@@ -4510,6 +4541,18 @@ static int ath11k_dp_rx_h_null_q_desc(st
 	 * rx with mac80211. Need not worry about cleaning up amsdu_list.
 	 */
 
+	if (ath11k_debugfs_is_extd_rx_stats_enabled(ar)) {
+		peer_id = ath11k_dp_rx_h_mpdu_start_peer_id(ar->ab, desc);
+		spin_lock_bh(&ar->ab->base_lock);
+		if (peer_id)
+			peer = ath11k_peer_find_by_id(ar->ab, rxcb->peer_id);
+		if (peer && peer->sta)
+			arsta = (struct ath11k_sta *)peer->sta->drv_priv;
+		spin_unlock_bh(&ar->ab->base_lock);
+		if (arsta)
+			atomic_inc(&arsta->drv_rx_pkts.pkts_frm_hw);
+	}
+
 	return 0;
 }
 
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -5590,6 +5590,8 @@ static void ath11k_mac_op_tx(struct ieee
 	struct ieee80211_key_conf *key = info->control.hw_key;
 	struct ath11k_mgmt_frame_stats *mgmt_stats = &arvif->mgmt_stats;
 	u32 info_flags = info->flags;
+	struct ieee80211_sta *sta = control->sta;
+	struct ath11k_sta *arsta = NULL;
 	bool is_prb_rsp;
 	u16 frm_type = 0;
 	int ret;
@@ -5653,6 +5655,15 @@ static void ath11k_mac_op_tx(struct ieee
 		ath11k_warn(ar->ab, "failed to transmit frame %d\n", ret);
 		ieee80211_free_txskb(ar->hw, skb);
 	}
+
+	if (ath11k_debugfs_is_extd_tx_stats_enabled(ar) && sta) {
+		arsta = (struct ath11k_sta *)sta->drv_priv;
+		if (arsta) {
+			atomic_inc(&arsta->drv_tx_pkts.pkts_in);
+			if (!ret)
+				atomic_inc(&arsta->drv_tx_pkts.pkts_out);
+		}
+	}
 }
 
 void ath11k_mac_drain_tx(struct ath11k *ar)
