From 9c99e124a279391dbe2cef66226fd4e86bde8f4d Mon Sep 17 00:00:00 2001
From: Maharaja Kennadyrajan <mkenna@codeaurora.org>
Date: Mon, 4 Jan 2021 23:46:53 +0530
Subject: [PATCH 1/2] ath11k/mac80211: Add support to account memory stats

Memory allocations in the driver & mac80211 are logged
and populate those values to the user space via debugfs.
This stats will give the snapshot of the memory being
used by the driver at the time of dumping these
memory stats.

Command:
cat /sys/kernel/debug/ath11k/ipq8074\ hw2.0/memory_stats

Sample output of the stats
MEMORY STATS IN BYTES:
malloc size : 6287583
ce_ring_alloc size: 109308
dma_alloc size:: 10831860
htc_skb_alloc size: 3840
wmi alloc size: 0
per peer object: 4644
rx_post_buf size: 5091840
Total size: 22329075

User can disable/enable the memory stats accounting with
the below command.

echo N > /sys/kernel/debug/ath11k/ipq8074\ hw2.0/enable_memory_stats
where N = 0 to disable logging, 1 to enable the logging.

Note: This should be enabled/disabled only after wifi is down.
User shouldn't enable/disable when the wifi is up to avoid
accounting the negative values which cause incorrect values
in the memory stats.

Command:

cat /sys/kernel/debug/ieee80211/phyX/memory_stats
memory stats: malloc_size: 108

Signed-off-by: Maharaja Kennadyrajan <mkenna@codeaurora.org>
---
 drivers/net/wireless/ath/ath11k/ce.c          |  24 ++++
 drivers/net/wireless/ath/ath11k/core.c        |   2 +-
 drivers/net/wireless/ath/ath11k/core.h        |  19 +++
 drivers/net/wireless/ath/ath11k/coredump.c    |  15 ++-
 drivers/net/wireless/ath/ath11k/dbring.c      |   3 +
 drivers/net/wireless/ath/ath11k/debugfs.c       | 115 ++++++++++++++++++
 drivers/net/wireless/ath/ath11k/debugfs.h       |  29 +++++
 drivers/net/wireless/ath/ath11k/debugfs_sta.c |   4 +
 drivers/net/wireless/ath/ath11k/dp.c          |  13 ++
 drivers/net/wireless/ath/ath11k/hal.c         |   6 +
 drivers/net/wireless/ath/ath11k/htc.c         |   5 +
 drivers/net/wireless/ath/ath11k/mac.c         |  15 ++-
 drivers/net/wireless/ath/ath11k/nss.c         |  46 +++++++
 drivers/net/wireless/ath/ath11k/peer.c        |   5 +
 drivers/net/wireless/ath/ath11k/wmi.c         |   4 +
 15 files changed, 302 insertions(+), 3 deletions(-)

--- a/drivers/net/wireless/ath/ath11k/ce.c
+++ b/drivers/net/wireless/ath/ath11k/ce.c
@@ -357,6 +357,9 @@ static int ath11k_ce_rx_post_pipe(struct
 			dev_kfree_skb_any(skb);
 			goto exit;
 		}
+
+		ATH11K_MEMORY_STATS_INC(ab, ce_rx_pipe, skb->truesize);
+
 	}
 
 exit:
@@ -425,6 +428,9 @@ static void ath11k_ce_recv_process_cb(st
 	__skb_queue_head_init(&list);
 	while (ath11k_ce_completed_recv_next(pipe, &skb, &nbytes) == 0) {
 		max_nbytes = skb->len + skb_tailroom(skb);
+
+		ATH11K_MEMORY_STATS_DEC(ab, ce_rx_pipe, skb->truesize);
+
 		dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr,
 				 max_nbytes, DMA_FROM_DEVICE);
 
@@ -618,6 +624,9 @@ ath11k_ce_alloc_ring(struct ath11k_base
 	if (ce_ring == NULL)
 		return ERR_PTR(-ENOMEM);
 
+	ATH11K_MEMORY_STATS_INC(ab, ce_ring_alloc,
+				struct_size(ce_ring, skb, nentries));
+
 	ce_ring->nentries = nentries;
 	ce_ring->nentries_mask = nentries - 1;
 
@@ -633,6 +642,9 @@ ath11k_ce_alloc_ring(struct ath11k_base
 		return ERR_PTR(-ENOMEM);
 	}
 
+	ATH11K_MEMORY_STATS_INC(ab, ce_ring_alloc,
+				nentries * desc_sz + CE_DESC_RING_ALIGN);
+
 	ce_ring->base_addr_ce_space_unaligned = base_addr;
 
 	ce_ring->base_addr_owner_space = PTR_ALIGN(
@@ -812,6 +824,9 @@ static void ath11k_ce_rx_pipe_cleanup(st
 			continue;
 
 		ring->skb[i] = NULL;
+
+		ATH11K_MEMORY_STATS_DEC(ab, ce_rx_pipe, skb->truesize);
+
 		dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr,
 				 skb->len + skb_tailroom(skb), DMA_FROM_DEVICE);
 		dev_kfree_skb_any(skb);
@@ -991,6 +1006,9 @@ void ath11k_ce_free_pipes(struct ath11k_
 					  CE_DESC_RING_ALIGN,
 					  pipe->src_ring->base_addr_owner_space,
 					  pipe->src_ring->base_addr_ce_space);
+			ATH11K_MEMORY_STATS_DEC(ab, ce_ring_alloc,
+						pipe->src_ring->nentries * desc_sz +
+						CE_DESC_RING_ALIGN);
 			kfree(pipe->src_ring);
 			pipe->src_ring = NULL;
 		}
@@ -1002,6 +1020,9 @@ void ath11k_ce_free_pipes(struct ath11k_
 					  CE_DESC_RING_ALIGN,
 					  pipe->dest_ring->base_addr_owner_space,
 					  pipe->dest_ring->base_addr_ce_space);
+			ATH11K_MEMORY_STATS_DEC(ab, ce_ring_alloc,
+						pipe->dest_ring->nentries * desc_sz +
+						CE_DESC_RING_ALIGN);
 			kfree(pipe->dest_ring);
 			pipe->dest_ring = NULL;
 		}
@@ -1014,6 +1035,9 @@ void ath11k_ce_free_pipes(struct ath11k_
 					  CE_DESC_RING_ALIGN,
 					  pipe->status_ring->base_addr_owner_space,
 					  pipe->status_ring->base_addr_ce_space);
+			ATH11K_MEMORY_STATS_DEC(ab, ce_ring_alloc,
+						pipe->status_ring->nentries * desc_sz +
+						CE_DESC_RING_ALIGN);
 			kfree(pipe->status_ring);
 			pipe->status_ring = NULL;
 		}
--- a/drivers/net/wireless/ath/ath11k/core.c
+++ b/drivers/net/wireless/ath/ath11k/core.c
@@ -1353,6 +1353,8 @@ int ath11k_core_pre_init(struct ath11k_b
 	if (nss_offload)
 		ab->nss.stats_enabled = 1;
 
+	ab->enable_memory_stats = ATH11K_DEBUG_ENABLE_MEMORY_STATS;
+
 	return 0;
 }
 EXPORT_SYMBOL(ath11k_core_pre_init);
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -801,6 +801,23 @@ struct ath11k_internal_pci {
 	wait_queue_head_t qgic_msi_waitq;
 };
 
+struct ath11k_memory_stats {
+	/* Account kzalloc and valloc */
+	atomic_t malloc_size;
+	/* Account dma_alloc in dp.c & hal.c */
+	atomic_t dma_alloc;
+	/* Account memory used in ce rings */
+	atomic_t ce_ring_alloc;
+	/* Account memory used in htc_send */
+	atomic_t htc_skb_alloc;
+	/* Account memory used in wmi tx skb alloc */
+	atomic_t wmi_tx_skb_alloc;
+	/* Account memory consumed for peer object */
+	atomic_t per_peer_object;
+	/* Account memory used in ce rx pipe */
+	atomic_t ce_rx_pipe;
+};
+
 /* Master structure to hold the hw data which may be used in core module */
 struct ath11k_base {
 	enum ath11k_hw_rev hw_rev;
@@ -878,6 +895,7 @@ struct ath11k_base {
 	enum ath11k_dfs_region dfs_region;
 #ifdef CPTCFG_ATH11K_DEBUGFS
 	struct dentry *debugfs_soc;
+	struct ath11k_memory_stats memory_stats;
 #endif
 	struct ath11k_soc_dp_stats soc_stats;
 
@@ -916,6 +934,7 @@ struct ath11k_base {
 	struct ath11k_num_vdevs_peers *num_vdevs_peers;
 	int userpd_id;
 	struct ath11k_internal_pci ipci;
+	bool enable_memory_stats;
 
 	/* must be last */
 	u8 drv_priv[0] __aligned(sizeof(void *));
--- a/drivers/net/wireless/ath/ath11k/coredump.c
+++ b/drivers/net/wireless/ath/ath11k/coredump.c
@@ -109,6 +109,8 @@ void ath11k_coredump_build_inline(struct
 	if (!buf)
 		return;
 
+	ATH11K_MEMORY_STATS_INC(ab, malloc_size, header_size);
+
 	file_data = (struct ath11k_dump_file_data *)buf;
 	strlcpy(file_data->df_magic, "ATH11K-FW-DUMP",
 	        sizeof(file_data->df_magic));
@@ -132,8 +134,10 @@ void ath11k_coredump_build_inline(struct
 	memcpy(file_data->seg, segments, num_seg * sizeof(*segments));
 
 	dump_state = vzalloc(sizeof(*dump_state));
-	if(!dump_state)
+	if(!dump_state) {
+		ATH11K_MEMORY_STATS_DEC(ab, malloc_size, header_size);
 		return;
+	}
 
 	dump_state->header = file_data;
 	dump_state->num_seg = num_seg;
@@ -145,6 +149,8 @@ void ath11k_coredump_build_inline(struct
 
 	/* Wait until the dump is read and free is called */
 	wait_for_completion(&dump_state->dump_done);
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, sizeof(*dump_state));
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, header_size);
 	vfree(dump_state);
 	vfree(file_data);
 }
@@ -274,14 +280,19 @@ void ath11k_coredump_qdss_dump(struct at
 		return;
 	}
 
+	ATH11K_MEMORY_STATS_INC(ab, malloc_size, len);
+
 	if (event_data->total_size &&
 	    event_data->total_size <= ab->qmi.qdss_mem[0].size)
 		dump = vzalloc(event_data->total_size);
 	if (!dump) {
+		ATH11K_MEMORY_STATS_DEC(ab, malloc_size, len);
 		vfree(segment);
 		return;
 	}
 
+	 ATH11K_MEMORY_STATS_INC(ab, malloc_size, event_data->total_size);
+
 	if (num_seg == 1) {
 		segment->len = event_data->mem_seg[0].size;
 		segment->vaddr = ab->qmi.qdss_mem[0].vaddr;
@@ -339,6 +350,8 @@ void ath11k_coredump_qdss_dump(struct at
 	}
 	ath11k_coredump_build_inline(ar_pci, segment, 1);
 out:
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, event_data->total_size);
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, len);
 	vfree(segment);
 	vfree(dump);
 }
--- a/drivers/net/wireless/ath/ath11k/dbring.c
+++ b/drivers/net/wireless/ath/ath11k/dbring.c
@@ -104,6 +104,7 @@ static int ath11k_dbring_fill_bufs(struc
 			break;
 		}
 		num_remain--;
+		ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size, size);
 	}
 
 	spin_unlock_bh(&srng->lock);
@@ -348,6 +349,8 @@ void ath11k_dbring_buf_cleanup(struct at
 		idr_remove(&ring->bufs_idr, buf_id);
 		dma_unmap_single(ar->ab->dev, buff->paddr,
 				 ring->buf_sz, DMA_FROM_DEVICE);
+		ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size, sizeof(*buff) +
+					ring->buf_sz + ring->buf_align - 1);
 		kfree(buff);
 	}
 
--- a/drivers/net/wireless/ath/ath11k/debugfs.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs.c
@@ -1358,6 +1358,8 @@ static ssize_t ath11k_debugfs_dump_soc_d
 	if (!buf)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ab, malloc_size, size);
+
 	len += scnprintf(buf + len, size - len, "SOC RX STATS:\n\n");
 	len += scnprintf(buf + len, size - len, "err ring pkts: %u\n",
 			 soc_stats->err_ring_pkts);
@@ -1399,6 +1401,8 @@ static ssize_t ath11k_debugfs_dump_soc_d
 	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 	kfree(buf);
 
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, size);
+
 	return retval;
 }
 
@@ -1476,6 +1480,106 @@ static const struct file_operations fops
        .open = simple_open,
 };
 
+static ssize_t
+ath11k_debug_read_enable_memory_stats(struct file *file,
+				      char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct ath11k_base *ab = file->private_data;
+	char buf[10];
+	size_t len;
+
+	len = scnprintf(buf, sizeof(buf), "%d\n", ab->enable_memory_stats);
+
+	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t
+ath11k_debug_write_enable_memory_stats(struct file *file,
+				       char __user *ubuf,
+				       size_t count, loff_t *ppos)
+{
+	struct ath11k_base *ab = file->private_data;
+	bool enable;
+	int ret;
+
+	if (kstrtobool_from_user(ubuf, count, &enable))
+		return -EINVAL;
+
+	if (enable == ab->enable_memory_stats) {
+		ret = count;
+		goto exit;
+	}
+
+	ab->enable_memory_stats = enable;
+	ret = count;
+exit:
+	return ret;
+}
+
+static const struct file_operations fops_enable_memory_stats = {
+	.read = ath11k_debug_read_enable_memory_stats,
+	.write = ath11k_debug_write_enable_memory_stats,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+	.open = simple_open,
+};
+
+static ssize_t ath11k_debug_dump_memory_stats(struct file *file,
+					      char __user *user_buf,
+					      size_t count, loff_t *ppos)
+{
+	struct ath11k_base *ab = file->private_data;
+	struct ath11k_memory_stats *memory_stats = &ab->memory_stats;
+	int len = 0, retval;
+	const int size = 4096;
+
+	char *buf;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len += scnprintf(buf + len, size - len, "MEMORY STATS IN BYTES:\n");
+	len += scnprintf(buf + len, size - len, "malloc size : %u\n",
+			 atomic_read(&memory_stats->malloc_size));
+	len += scnprintf(buf + len, size - len, "ce_ring_alloc size: %u\n",
+			 atomic_read(&memory_stats->ce_ring_alloc));
+	len += scnprintf(buf + len, size - len, "dma_alloc size:: %u\n",
+			 atomic_read(&memory_stats->dma_alloc));
+	len += scnprintf(buf + len, size - len, "htc_skb_alloc size: %u\n",
+			 atomic_read(&memory_stats->htc_skb_alloc));
+	len += scnprintf(buf + len, size - len, "wmi tx skb alloc size: %u\n",
+			 atomic_read(&memory_stats->wmi_tx_skb_alloc));
+	len += scnprintf(buf + len, size - len, "per peer object: %u\n",
+			 atomic_read(&memory_stats->per_peer_object));
+	len += scnprintf(buf + len, size - len, "rx_post_buf size: %u\n",
+			 atomic_read(&memory_stats->ce_rx_pipe));
+	len += scnprintf(buf + len, size - len, "Total size: %u\n\n",
+			 (atomic_read(&memory_stats->malloc_size) +
+			 atomic_read(&memory_stats->ce_ring_alloc) +
+			 atomic_read(&memory_stats->dma_alloc) +
+			 atomic_read(&memory_stats->htc_skb_alloc) +
+			 atomic_read(&memory_stats->wmi_tx_skb_alloc) +
+			 atomic_read(&memory_stats->per_peer_object) +
+			 atomic_read(&memory_stats->ce_rx_pipe)));
+
+	if (len > size)
+		len = size;
+
+	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+	kfree(buf);
+
+	return retval;
+}
+
+static const struct file_operations fops_memory_stats = {
+	.read = ath11k_debug_dump_memory_stats,
+	.open = simple_open,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
 int ath11k_debugfs_pdev_create(struct ath11k_base *ab)
 {
 	if (test_bit(ATH11K_FLAG_REGISTERED, &ab->dev_flags))
@@ -1639,6 +1743,12 @@ static ssize_t ath11k_write_pktlog_filte
 		}
 	}
 
+	debugfs_create_file("enable_memory_stats", 0600, ab->debugfs_soc,
+			    ab, &fops_enable_memory_stats);
+
+	debugfs_create_file("memory_stats", 0600, ab->debugfs_soc, ab,
+			    &fops_memory_stats);
+
 
 #define HTT_RX_FILTER_TLV_LITE_MODE \
 			(HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \
@@ -2225,6 +2335,8 @@ static ssize_t ath11k_dump_mgmt_stats(st
 	if (!buf)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size, size);
+
 	mutex_lock(&ar->conf_mutex);
 	spin_lock_bh(&ar->data_lock);
 
@@ -2275,6 +2387,9 @@ static ssize_t ath11k_dump_mgmt_stats(st
 	ret = simple_read_from_buffer(ubuf, count, ppos, buf, len);
 	mutex_unlock(&ar->conf_mutex);
 	kfree(buf);
+
+	ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size, size);
+
 	return ret;
 }
 
--- a/drivers/net/wireless/ath/ath11k/debugfs.h
+++ b/drivers/net/wireless/ath/ath11k/debugfs.h
@@ -10,6 +10,7 @@
 
 #define ATH11K_TX_POWER_MAX_VAL	70
 #define ATH11K_TX_POWER_MIN_VAL	0
+#define ATH11K_DEBUG_ENABLE_MEMORY_STATS 1
 
 /* htt_dbg_ext_stats_type */
 enum ath11k_dbg_htt_ext_stats_type {
@@ -163,6 +164,24 @@ void ath11k_debugfs_wmi_ctrl_stats(struc
 void ath11k_wmi_crl_path_stats_list_free(struct list_head *head);
 
 #ifdef CPTCFG_ATH11K_DEBUGFS
+#define ATH11K_MEMORY_STATS_INC(_struct, _field, _size)			\
+do {									\
+	if (ath11k_debug_is_memory_stats_enabled(_struct)) 		\
+		atomic_add(_size, &_struct->memory_stats._field);	\
+} while(0)
+
+#define ATH11K_MEMORY_STATS_DEC(_struct, _field, _size)			\
+do {									\
+	if (ath11k_debug_is_memory_stats_enabled(_struct))		\
+		atomic_sub(_size, &_struct->memory_stats._field);	\
+} while(0)
+
+#else
+#define ATH11K_MEMORY_STATS_INC(_struct, _field, _size)
+#define ATH11K_MEMORY_STATS_DEC(_struct, _field, _size)
+#endif
+
+#ifdef CPTCFG_ATH11K_DEBUGFS
 int ath11k_debugfs_create(void);
 void ath11k_debugfs_destroy(void);
 int ath11k_debugfs_soc_create(struct ath11k_base *ab);
@@ -208,6 +227,11 @@ static inline int ath11k_debugfs_rx_filt
 	return ar->debug.rx_filter;
 }
 
+static inline int ath11k_debug_is_memory_stats_enabled(struct ath11k_base *ab)
+{
+	return ab->enable_memory_stats;
+}
+
 #else
 static inline int ath11k_debugfs_create(void)
 {
@@ -285,6 +309,11 @@ static inline bool ath11k_debugfs_is_pkt
 	return false;
 }
 
+static inline int ath11k_debug_is_memory_stats_enabled(struct ath11k_base *ab)
+{
+	return 0;
+}
+
 static inline int ath11k_debugfs_rx_filter(struct ath11k *ar)
 {
 	return 0;
--- a/drivers/net/wireless/ath/ath11k/debugfs_sta.c
+++ b/drivers/net/wireless/ath/ath11k/debugfs_sta.c
@@ -492,6 +492,8 @@ static ssize_t ath11k_dbg_sta_dump_rx_st
 	if (!buf)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size, size);
+
 	he_rates_avail = (rx_stats->pream_cnt[HAL_RX_PREAMBLE_11AX] > 1) ? 1 : 0;
 	rate_table_len = he_rates_avail ? ATH11K_RX_RATE_TABLE_11AX_NUM :
 					      ATH11K_RX_RATE_TABLE_NUM;
@@ -636,6 +638,8 @@ static ssize_t ath11k_dbg_sta_dump_rx_st
 	retval = simple_read_from_buffer(user_buf, count, ppos, buf, len);
 	kfree(buf);
 
+	ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size, size);
+
 	mutex_unlock(&ar->conf_mutex);
 	return retval;
 }
--- a/drivers/net/wireless/ath/ath11k/dp.c
+++ b/drivers/net/wireless/ath/ath11k/dp.c
@@ -109,6 +109,8 @@ void ath11k_dp_srng_cleanup(struct ath11
 	dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned,
 			  ring->paddr_unaligned);
 
+	ATH11K_MEMORY_STATS_DEC(ab, dma_alloc, ring->size);
+
 	ring->vaddr_unaligned = NULL;
 }
 
@@ -253,6 +255,8 @@ int ath11k_dp_srng_setup(struct ath11k_b
 	if (!ring->vaddr_unaligned)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ab, dma_alloc, ring->size);
+
 	ring->vaddr = PTR_ALIGN(ring->vaddr_unaligned, HAL_RING_BASE_ALIGN);
 	ring->paddr = ring->paddr_unaligned + ((unsigned long)ring->vaddr -
 		      (unsigned long)ring->vaddr_unaligned);
@@ -485,6 +489,7 @@ static void ath11k_dp_scatter_idle_link_
 		dma_free_coherent(ab->dev, HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX,
 				  slist[i].vaddr, slist[i].paddr);
 		slist[i].vaddr = NULL;
+		ATH11K_MEMORY_STATS_DEC(ab, dma_alloc, HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX);
 	}
 }
 
@@ -522,6 +527,7 @@ static int ath11k_dp_scatter_idle_link_d
 			ret = -ENOMEM;
 			goto err;
 		}
+		ATH11K_MEMORY_STATS_INC(ab, dma_alloc, HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX);
 	}
 
 	scatter_idx = 0;
@@ -576,6 +582,7 @@ ath11k_dp_link_desc_bank_free(struct ath
 					  link_desc_banks[i].vaddr_unaligned,
 					  link_desc_banks[i].paddr_unaligned);
 			link_desc_banks[i].vaddr_unaligned = NULL;
+			ATH11K_MEMORY_STATS_DEC(ab, dma_alloc, link_desc_banks[i].size);
 		}
 	}
 }
@@ -609,6 +616,7 @@ static int ath11k_dp_link_desc_bank_allo
 				     ((unsigned long)desc_bank[i].vaddr -
 				      (unsigned long)desc_bank[i].vaddr_unaligned);
 		desc_bank[i].size = desc_sz;
+		ATH11K_MEMORY_STATS_INC(ab, dma_alloc, desc_bank[i].size);
 	}
 
 	return 0;
@@ -1023,8 +1031,11 @@ static int ath11k_dp_tx_pending_cleanup(
 void ath11k_dp_free(struct ath11k_base *ab)
 {
 	struct ath11k_dp *dp = &ab->dp;
+	size_t size = 0;
 	int i;
 
+	size = sizeof(struct hal_wbm_release_ring) * DP_TX_COMP_RING_SIZE;
+
 	ath11k_dp_link_desc_cleanup(ab, dp->link_desc_banks,
 				    HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring);
 
@@ -1038,6 +1049,7 @@ void ath11k_dp_free(struct ath11k_base *
 			     ath11k_dp_tx_pending_cleanup, ab);
 		idr_destroy(&dp->tx_ring[i].txbuf_idr);
 		spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock);
+		ATH11K_MEMORY_STATS_DEC(ab, malloc_size, size);
 		kfree(dp->tx_ring[i].tx_status);
 	}
 
@@ -1095,6 +1107,7 @@ int ath11k_dp_alloc(struct ath11k_base *
 			ret = -ENOMEM;
 			goto fail_cmn_srng_cleanup;
 		}
+		ATH11K_MEMORY_STATS_INC(ab, malloc_size, size);
 	}
 
 	for (i = 0; i < HAL_DSCP_TID_MAP_TBL_NUM_ENTRIES_MAX; i++)
--- a/drivers/net/wireless/ath/ath11k/hal.c
+++ b/drivers/net/wireless/ath/ath11k/hal.c
@@ -200,6 +200,8 @@ static int ath11k_hal_alloc_cont_rdp(str
 	if (!hal->rdp.vaddr)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ab, dma_alloc, size);
+
 	return 0;
 }
 
@@ -214,6 +216,7 @@ static void ath11k_hal_free_cont_rdp(str
 	size = sizeof(u32) * HAL_SRNG_RING_ID_MAX;
 	dma_free_coherent(ab->dev, size,
 			  hal->rdp.vaddr, hal->rdp.paddr);
+	ATH11K_MEMORY_STATS_DEC(ab, dma_alloc, size);
 	hal->rdp.vaddr = NULL;
 }
 
@@ -228,6 +231,8 @@ static int ath11k_hal_alloc_cont_wrp(str
 	if (!hal->wrp.vaddr)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ab, dma_alloc, size);
+
 	return 0;
 }
 
@@ -242,6 +247,7 @@ static void ath11k_hal_free_cont_wrp(str
 	size = sizeof(u32) * HAL_SRNG_NUM_LMAC_RINGS;
 	dma_free_coherent(ab->dev, size,
 			  hal->wrp.vaddr, hal->wrp.paddr);
+	ATH11K_MEMORY_STATS_DEC(ab, dma_alloc, size);
 	hal->wrp.vaddr = NULL;
 }
 
--- a/drivers/net/wireless/ath/ath11k/htc.c
+++ b/drivers/net/wireless/ath/ath11k/htc.c
@@ -28,6 +28,7 @@ struct sk_buff *ath11k_htc_alloc_skb(str
 static void ath11k_htc_control_tx_complete(struct ath11k_base *ab,
 					   struct sk_buff *skb)
 {
+	ATH11K_MEMORY_STATS_DEC(ab, htc_skb_alloc, skb->truesize);
 	kfree_skb(skb);
 }
 
@@ -650,6 +651,8 @@ int ath11k_htc_connect_service(struct at
 		return status;
 	}
 
+	ATH11K_MEMORY_STATS_INC(ab, htc_skb_alloc, skb->truesize);
+
 	/* wait for response */
 	time_left = wait_for_completion_timeout(&htc->ctl_resp,
 						ATH11K_HTC_CONN_SVC_TIMEOUT_HZ);
@@ -773,6 +776,8 @@ int ath11k_htc_start(struct ath11k_htc *
 		return status;
 	}
 
+	 ATH11K_MEMORY_STATS_INC(ab, htc_skb_alloc, skb->truesize);
+
 	return 0;
 }
 
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -3535,6 +3535,8 @@ static int ath11k_mac_op_hw_scan(struct
 		goto exit;
 	}
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size, sizeof(*arg));
+
 	ath11k_wmi_start_scan_init(ar, arg);
 	arg->vdev_id = arvif->vdev_id;
 	arg->scan_id = ATH11K_SCAN_ID;
@@ -3546,6 +3548,8 @@ static int ath11k_mac_op_hw_scan(struct
 			memcpy(arg->extraie.ptr, req->ie, req->ie_len);
 	}
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size, req->ie_len);
+
 	if (req->n_ssids) {
 		arg->num_ssids = req->n_ssids;
 		for (i = 0; i < arg->num_ssids; i++) {
@@ -3597,8 +3601,13 @@ static int ath11k_mac_op_hw_scan(struct
 
 exit:
 	if (arg) {
-		if (arg->extraie.ptr)
+		if (arg->extraie.ptr) {
+			ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size, req->ie_len);
 			kfree(arg->extraie.ptr);
+		}
+
+		ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size, sizeof(*arg));
+
 		kfree(arg);
 	}
 
@@ -6770,6 +6779,8 @@ ath11k_mac_update_active_vif_chan(struct
 	if (!arg.vifs)
 		return;
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size, sizeof(arg.vifs[0]));
+
 	ieee80211_iterate_active_interfaces_atomic(ar->hw,
 						   IEEE80211_IFACE_ITER_NORMAL,
 						   ath11k_mac_change_chanctx_fill_iter,
@@ -6777,6 +6788,8 @@ ath11k_mac_update_active_vif_chan(struct
 
 	ath11k_mac_update_vif_chan(ar, arg.vifs, arg.n_vifs);
 
+	ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size, sizeof(arg.vifs[0]));
+
 	kfree(arg.vifs);
 }
 
--- a/drivers/net/wireless/ath/ath11k/nss.c
+++ b/drivers/net/wireless/ath/ath11k/nss.c
@@ -876,6 +876,8 @@ int ath11k_nss_vdev_set_cmd(struct ath11
 	if (!vdev_msg)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size, sizeof(*vdev_msg));
+
 	/* TODO: Convert to function for conversion in case of many
 	 * such commands
 	 */
@@ -906,6 +908,7 @@ int ath11k_nss_vdev_set_cmd(struct ath11
 	ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev set cmd success cmd:%d val:%d\n",
 		   cmd, val);
 free:
+	ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size, sizeof(*vdev_msg));
 	kfree(vdev_msg);
 	return status;
 }
@@ -922,6 +925,9 @@ static int ath11k_nss_vdev_configure(str
 	if (!vdev_msg)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size,
+				sizeof(struct nss_wifi_vdev_msg));
+
 	vdev_cfg = &vdev_msg->msg.vdev_config;
 
 	vdev_cfg->radio_ifnum = ar->nss.if_num;
@@ -957,6 +963,8 @@ static int ath11k_nss_vdev_configure(str
 
 	ret = 0;
 free:
+	ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size,
+				sizeof(struct nss_wifi_vdev_msg));
 	kfree(vdev_msg);
 
 	return ret;
@@ -1180,6 +1188,9 @@ int ath11k_nss_vdev_up(struct ath11k_vif
 	if (!vdev_msg)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size,
+				sizeof(struct nss_wifi_vdev_msg));
+
 	vdev_en = &vdev_msg->msg.vdev_enable;
 
 	ether_addr_copy(vdev_en->mac_addr, arvif->vif->addr);
@@ -1198,6 +1209,8 @@ int ath11k_nss_vdev_up(struct ath11k_vif
 
 	ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev up tx msg success\n");
 free:
+	ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size,
+				sizeof(struct nss_wifi_vdev_msg));
 	kfree(vdev_msg);
 	return ret;
 }
@@ -1220,6 +1233,8 @@ int ath11k_nss_vdev_down(struct ath11k_v
 	if (!vdev_msg)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size,
+				sizeof(struct nss_wifi_vdev_msg));
 	nss_wifi_vdev_msg_init(vdev_msg, arvif->nss.if_num,
 			       NSS_WIFI_VDEV_INTERFACE_DOWN_MSG,
 			       sizeof(struct nss_wifi_vdev_disable_msg),
@@ -1234,6 +1249,8 @@ int ath11k_nss_vdev_down(struct ath11k_v
 
 	ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss vdev down tx msg success\n");
 free:
+	ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size,
+				sizeof(struct nss_wifi_vdev_msg));
 	kfree(vdev_msg);
 	return ret;
 }
@@ -1257,6 +1274,9 @@ int ath11k_nss_set_peer_sec_type(struct
 	if (!wlmsg)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, malloc_size,
+				sizeof(struct nss_wifili_msg));
+
 	sec_msg = &wlmsg->msg.securitymsg;
 	sec_msg->peer_id = peer->peer_id;
 
@@ -1288,6 +1308,8 @@ int ath11k_nss_set_peer_sec_type(struct
 	ath11k_dbg(ar->ab, ATH11K_DBG_NSS, "nss peer id %d security cfg complete\n",
 		   peer->peer_id);
 free:
+	ATH11K_MEMORY_STATS_DEC(ar->ab, malloc_size,
+				sizeof(struct nss_wifili_msg));
 	kfree(wlmsg);
 	return status;
 }
@@ -1886,6 +1908,7 @@ static void ath11k_nss_tx_desc_mem_free(
 				  ab->nss.tx_desc_vaddr[i],
 				  ab->nss.tx_desc_paddr[i]);
 		ab->nss.tx_desc_vaddr[i] = NULL;
+		ATH11K_MEMORY_STATS_DEC(ab, dma_alloc, ab->nss.tx_desc_size[i]);
 	}
 
 	ath11k_dbg(ab, ATH11K_DBG_NSS, "allocated tx desc mem freed\n");
@@ -1917,6 +1940,8 @@ static int ath11k_nss_tx_desc_mem_alloc(
 		ab->nss.tx_desc_size[curr_page_idx] = alloc_size;
 		curr_page_idx++;
 
+		ATH11K_MEMORY_STATS_INC(ab, dma_alloc, alloc_size);
+
 		ath11k_dbg(ab, ATH11K_DBG_NSS,
 			   "curr page %d, allocated %d, total allocated %d\n",
 			   curr_page_idx, alloc_size, i + alloc_size);
@@ -2093,6 +2118,8 @@ static int ath11k_nss_init(struct ath11k
 	if (!wlmsg)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ab, malloc_size, sizeof(struct nss_wifili_msg));
+
 	wim = &wlmsg->msg.init;
 
 	wim->target_type = target_type;
@@ -2211,6 +2238,7 @@ unregister:
 	nss_unregister_wifili_if(ab->nss.if_num);
 free:
 	ath11k_nss_tx_desc_mem_free(ab);
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, sizeof(struct nss_wifili_msg));
 	kfree(wlmsg);
 	return -EINVAL;
 }
@@ -2316,6 +2344,8 @@ int ath11k_nss_pdev_init(struct ath11k_b
 		goto unregister;
 	}
 
+	ATH11K_MEMORY_STATS_INC(ab, malloc_size, sizeof(struct nss_wifili_msg));
+
 	pdevmsg = &wlmsg->msg.pdevmsg;
 
 	pdevmsg->radio_id = radio_id;
@@ -2361,6 +2391,8 @@ int ath11k_nss_pdev_init(struct ath11k_b
 		goto free;
 	}
 
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, sizeof(struct nss_wifili_msg));
+
 	kfree(wlmsg);
 
 	/* Disable nss sojourn stats by default */
@@ -2379,6 +2411,7 @@ int ath11k_nss_pdev_init(struct ath11k_b
 	return 0;
 
 free:
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, sizeof(struct nss_wifili_msg));
 	kfree(wlmsg);
 unregister:
 	nss_unregister_wifili_radio_if(ar->nss.if_num);
@@ -2401,6 +2434,8 @@ int ath11k_nss_start(struct ath11k_base
 	if (!wlmsg)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ab, malloc_size, sizeof(struct nss_wifili_msg));
+
 	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
 
 	/* Empty message for NSS Start message */
@@ -2441,6 +2476,7 @@ int ath11k_nss_start(struct ath11k_base
 	ath11k_dbg(ab, ATH11K_DBG_NSS, "nss start success\n");
 
 free:
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, sizeof(struct nss_wifili_msg));
 	kfree(wlmsg);
 	return ret;
 }
@@ -2459,6 +2495,8 @@ static void ath11k_nss_reset(struct ath1
 		return;
 	}
 
+	ATH11K_MEMORY_STATS_INC(ab, malloc_size, sizeof(struct nss_wifili_msg));
+
 	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
 
 	/* Empty message for NSS Reset message */
@@ -2497,6 +2535,7 @@ static void ath11k_nss_reset(struct ath1
 	nss_unregister_wifili_if(ab->nss.if_num);
 
 free:
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, sizeof(struct nss_wifili_msg));
 	kfree(wlmsg);
 }
 
@@ -2512,6 +2551,8 @@ static int ath11k_nss_stop(struct ath11k
 	if (!wlmsg)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ab, malloc_size, sizeof(struct nss_wifili_msg));
+
 	msg_cb = (nss_wifili_msg_callback_t)ath11k_nss_wifili_event_receive;
 
 	/* Empty message for Stop command */
@@ -2551,6 +2592,8 @@ static int ath11k_nss_stop(struct ath11k
 	/* NSS Stop success */
 	ret = 0;
 free:
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, sizeof(struct nss_wifili_msg));
+
 	kfree(wlmsg);
 	return ret;
 }
@@ -2576,6 +2619,8 @@ int ath11k_nss_pdev_deinit(struct ath11k
 	if (!wlmsg)
 		return -ENOMEM;
 
+	ATH11K_MEMORY_STATS_INC(ab, malloc_size, sizeof(struct nss_wifili_msg));
+
 	deinit = &wlmsg->msg.pdevdeinit;
 	deinit->ifnum = radio_id;
 
@@ -2618,6 +2663,7 @@ int ath11k_nss_pdev_deinit(struct ath11k
 	nss_dynamic_interface_dealloc_node(ar->nss.if_num, dyn_if_type);
 	nss_unregister_wifili_radio_if(ar->nss.if_num);
 free:
+	ATH11K_MEMORY_STATS_DEC(ab, malloc_size, sizeof(struct nss_wifili_msg));
 	kfree(wlmsg);
 	return ret;
 }
--- a/drivers/net/wireless/ath/ath11k/peer.c
+++ b/drivers/net/wireless/ath/ath11k/peer.c
@@ -633,6 +633,9 @@ int ath11k_peer_delete(struct ath11k *ar
 	if (ret)
 		return ret;
 
+	ATH11K_MEMORY_STATS_DEC(ar->ab, per_peer_object,
+				sizeof(struct ath11k_peer));
+
 	ar->num_peers--;
 
 	return 0;
@@ -729,6 +732,8 @@ int ath11k_peer_create(struct ath11k *ar
 		ath11k_dp_sta_tx_attach(arsta, peer->peer_id);
 	}
 
+	ATH11K_MEMORY_STATS_INC(ar->ab, per_peer_object, sizeof(*peer));
+
 	ar->num_peers++;
 
 	spin_unlock_bh(&ar->ab->base_lock);
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -599,6 +599,8 @@ struct sk_buff *ath11k_wmi_alloc_skb(str
 	if (!skb)
 		return NULL;
 
+	ATH11K_MEMORY_STATS_INC(ab, wmi_tx_skb_alloc, skb->truesize);
+
 	skb_reserve(skb, WMI_SKB_HEADROOM);
 	if (!IS_ALIGNED((unsigned long)skb->data, 4))
 		ath11k_warn(ab, "unaligned WMI skb data\n");
@@ -6666,6 +6668,7 @@ static void ath11k_wmi_htc_tx_complete(s
 	if (wmi_ep_count > ab->hw_params.max_radios)
 		goto out;
 
+	ATH11K_MEMORY_STATS_DEC(ab, wmi_tx_skb_alloc, skb->truesize);
 	dev_kfree_skb(skb);
 
 	for (i = 0; i < ab->htc.wmi_ep_count; i++) {
@@ -6679,6 +6682,7 @@ static void ath11k_wmi_htc_tx_complete(s
 
 	return;
 out:
+	ATH11K_MEMORY_STATS_DEC(ab, wmi_tx_skb_alloc, skb->truesize);
 	dev_kfree_skb(skb);
 }
 
