Browse Source

qcacmn: Implementation of SKB Frag Debug Framework

SKB frag debug framework is required for debug purpose of frag based
approach in monitor mode

Change-Id: Ic7a5a2c3d7397c4d7dd2c5db32802b0f694e6101
CRs-Fixed: 2736136
Harsh Kumar Bijlani 5 years ago
parent
commit
d47e448d58

+ 87 - 0
qdf/inc/qdf_nbuf.h

@@ -3756,6 +3756,80 @@ static inline void qdf_nbuf_trim_add_frag_size(qdf_nbuf_t nbuf, uint8_t idx,
 	__qdf_nbuf_trim_add_frag_size(nbuf, idx, size, truesize);
 	__qdf_nbuf_trim_add_frag_size(nbuf, idx, size, truesize);
 }
 }
 
 
+#ifdef NBUF_FRAG_MEMORY_DEBUG
+
+#define qdf_nbuf_move_frag_page_offset(f, i, o) \
+	qdf_nbuf_move_frag_page_offset_debug(f, i, o, __func__, __LINE__)
+
+/**
+ * qdf_nbuf_move_frag_page_offset_debug() - Move frag page_offset by size
+ *          and adjust length by size.
+ * @nbuf: qdf_nbuf_t
+ * @idx: Frag index
+ * @offset: Frag page offset should be moved by offset.
+ *      +Ve - Move offset forward.
+ *      -Ve - Move offset backward.
+ * @func: Caller function name
+ * @line: Caller function line no.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS qdf_nbuf_move_frag_page_offset_debug(qdf_nbuf_t nbuf, uint8_t idx,
+						int offset, const char *func,
+						uint32_t line);
+
+#define qdf_nbuf_add_rx_frag(f, b, o, l, s, r) \
+	qdf_nbuf_add_rx_frag_debug(f, b, o, l, s, r, __func__, __LINE__)
+
+/**
+ * qdf_nbuf_add_rx_frag_debug() - Add frag to nbuf at index frag_idx
+ * @buf: Frag pointer needs to be added in nbuf
+ * @nbuf: qdf_nbuf_t where frag will be added
+ * @offset: Offset in frag to be added to nbuf_frags
+ * @frag_len: Frag length
+ * @truesize: truesize
+ * @take_frag_ref: Whether to take ref for frag or not
+ *      This bool must be set as per below comdition:
+ *      1. False: If this frag is being added in any nbuf
+ *              for the first time after allocation
+ *      2. True: If frag is already attached part of any
+ *              nbuf
+ * @func: Caller function name
+ * @line: Caller function line no.
+ *
+ * Return: none
+ */
+void qdf_nbuf_add_rx_frag_debug(qdf_frag_t buf, qdf_nbuf_t nbuf,
+				int offset, int frag_len,
+				unsigned int truesize, bool take_frag_ref,
+				const char *func, uint32_t line);
+
+/**
+ * qdf_net_buf_debug_acquire_frag() - Add frag nodes to frag debug tracker
+ *	when nbuf is received from network stack
+ * @buf: qdf_nbuf_t
+ * @func: Caller function name
+ * @line: Caller function line no.
+ *
+ * Return: none
+ */
+void qdf_net_buf_debug_acquire_frag(qdf_nbuf_t buf, const char *func,
+				    uint32_t line);
+
+/**
+ * qdf_net_buf_debug_release_frag() - Update frag nodes in frag debug tracker
+ *	when nbuf is sent to network stack
+ * @buf: qdf_nbuf_t
+ * @func: Caller function name
+ * @line: Caller function line no.
+ *
+ * Return: none
+ */
+void qdf_net_buf_debug_release_frag(qdf_nbuf_t buf, const char *func,
+				    uint32_t line);
+
+#else /* NBUF_FRAG_MEMORY_DEBUG */
+
 /**
 /**
  * qdf_nbuf_move_frag_page_offset() - Move frag page_offset by size
  * qdf_nbuf_move_frag_page_offset() - Move frag page_offset by size
  *          and adjust length by size.
  *          and adjust length by size.
@@ -3797,6 +3871,19 @@ static inline void qdf_nbuf_add_rx_frag(qdf_frag_t buf, qdf_nbuf_t nbuf,
 			       frag_len, truesize, take_frag_ref);
 			       frag_len, truesize, take_frag_ref);
 }
 }
 
 
+static inline void qdf_net_buf_debug_acquire_frag(qdf_nbuf_t buf,
+						  const char *func,
+						  uint32_t line)
+{
+}
+
+static inline void qdf_net_buf_debug_release_frag(qdf_nbuf_t buf,
+						  const char *func,
+						  uint32_t line)
+{
+}
+#endif /* NBUF_FRAG_MEMORY_DEBUG */
+
 #ifdef CONFIG_NBUF_AP_PLATFORM
 #ifdef CONFIG_NBUF_AP_PLATFORM
 #include <i_qdf_nbuf_api_w.h>
 #include <i_qdf_nbuf_api_w.h>
 #else
 #else

+ 217 - 19
qdf/inc/qdf_nbuf_frag.h

@@ -24,6 +24,7 @@
 #ifndef _QDF_NBUF_FRAG_H
 #ifndef _QDF_NBUF_FRAG_H
 #define _QDF_NBUF_FRAG_H
 #define _QDF_NBUF_FRAG_H
 
 
+#include <qdf_util.h>
 #include <i_qdf_trace.h>
 #include <i_qdf_trace.h>
 #include <i_qdf_nbuf_frag.h>
 #include <i_qdf_nbuf_frag.h>
 
 
@@ -37,6 +38,222 @@ typedef __qdf_frag_t qdf_frag_t;
  */
  */
 #define QDF_NBUF_MAX_FRAGS __QDF_NBUF_MAX_FRAGS
 #define QDF_NBUF_MAX_FRAGS __QDF_NBUF_MAX_FRAGS
 
 
+#ifdef NBUF_FRAG_MEMORY_DEBUG
+/**
+ * qdf_frag_debug_init() - Initialize frag debug tracker
+ *
+ * Return: none
+ */
+void qdf_frag_debug_init(void);
+
+/**
+ * qdf_frag_debug_exit() - Destroy frag debug tracker
+ *
+ * Return: none
+ */
+void qdf_frag_debug_exit(void);
+
+/**
+ * qdf_frag_debug_add_node() - Add frag node in the debug hash table
+ * @fragp: Pointer to frag
+ * @func_name: Caller function name
+ * @line_num: Caller function line no.
+ *
+ * Return: none
+ */
+void qdf_frag_debug_add_node(qdf_frag_t fragp, const char *func_name,
+			     uint32_t line_num);
+
+/**
+ * qdf_frag_debug_refcount_inc() - Increment refcount for frag node
+ * @fragp: Pointer to frag
+ * @func_name: Caller function name
+ * @line_num: Caller function line no.
+ *
+ * Return: none
+ */
+void qdf_frag_debug_refcount_inc(qdf_frag_t fragp, const char *func_name,
+				 uint32_t line_num);
+
+/**
+ * qdf_frag_debug_refcount_dec() - Decrement refcount for frag node
+ * @fragp: Pointer to frag
+ * @func_name: Caller function name
+ * @line_num: Caller function line no.
+ *
+ * Return: none
+ */
+void qdf_frag_debug_refcount_dec(qdf_frag_t fragp, const char *func_name,
+				 uint32_t line_num);
+
+/**
+ * qdf_frag_debug_delete_node() - Remove frag node from debug hash table
+ * @fragp: Pointer to frag
+ * @func_name: Caller function name
+ * @line_num: Caller function line no.
+ *
+ * Return: none
+ */
+void qdf_frag_debug_delete_node(qdf_frag_t fragp, const char *func_name,
+				uint32_t line_num);
+
+/**
+ * qdf_frag_debug_update_addr() - Update frag address in debug tracker
+ * @p_fragp: Previous frag address
+ * @n_fragp: New frag address
+ * @func_name: Caller function name
+ * @line_num: Caller function line no.
+ *
+ * Return: none
+ */
+void qdf_frag_debug_update_addr(qdf_frag_t p_fragp, qdf_frag_t n_fragp,
+				const char *func_name, uint32_t line_num);
+
+#define qdf_frag_alloc(s) \
+	qdf_frag_alloc_debug(s, __func__, __LINE__)
+
+/**
+ * qdf_frag_alloc_debug() - Allocate frag memory
+ * @fragsz: Size of frag memory to be allocated
+ * @func_name: Caller function name
+ * @line_num: Caller function line no.
+ *
+ * Return: Allocated frag address
+ */
+qdf_frag_t qdf_frag_alloc_debug(unsigned int fragsz, const char *func_name,
+				uint32_t line_num);
+
+#define qdf_frag_free(p) \
+	qdf_frag_free_debug(p, __func__, __LINE__)
+
+/**
+ * qdf_frag_free_debug() - Free allocated frag memory
+ * @vaddr: Frag address to be freed
+ * @func_name: Caller function name
+ * @line_num: Caller function line no.
+ *
+ * Return: none
+ */
+void qdf_frag_free_debug(qdf_frag_t vaddr, const char *func_name,
+			 uint32_t line_num);
+
+#else /* NBUF_FRAG_MEMORY_DEBUG */
+
+static inline void qdf_frag_debug_init(void)
+{
+}
+
+static inline void qdf_frag_debug_exit(void)
+{
+}
+
+static inline void qdf_frag_debug_add_node(qdf_frag_t fragp,
+					   const char *func_name,
+					   uint32_t line_num)
+{
+}
+
+static inline void qdf_frag_debug_refcount_inc(qdf_frag_t fragp,
+					       const char *func_name,
+					       uint32_t line_num)
+{
+}
+
+static inline void qdf_frag_debug_refcount_dec(qdf_frag_t fragp,
+					       const char *func_name,
+					       uint32_t line_num)
+{
+}
+
+static inline void qdf_frag_debug_delete_node(qdf_frag_t fragp,
+					      const char *func_name,
+					      uint32_t line_num)
+{
+}
+
+static inline void qdf_frag_debug_update_addr(qdf_frag_t p_fragp,
+					      qdf_frag_t n_fragp,
+					      const char *func_name,
+					      uint32_t line_num)
+{
+}
+
+/**
+ * qdf_frag_alloc() - Allocate frag memory
+ * @fragsz: Size of frag memory to be allocated
+ *
+ * Return: Allocated frag address
+ */
+static inline qdf_frag_t qdf_frag_alloc(unsigned int fragsz)
+{
+	return __qdf_frag_alloc(fragsz);
+}
+
+/**
+ * qdf_frag_free() - Free allocated frag memory
+ * @vaddr: Frag address to be freed
+ *
+ * Return: none
+ */
+static inline void qdf_frag_free(qdf_frag_t vaddr)
+{
+	__qdf_frag_free(vaddr);
+}
+
+#endif /* NBUF_FRAG_MEMORY_DEBUG */
+
+/**
+ * qdf_frag_count_get() - Get global frag gauge
+ *
+ * Return: Global frag gauge
+ */
+static inline uint32_t qdf_frag_count_get(void)
+{
+	return __qdf_frag_count_get();
+}
+
+/**
+ * qdf_frag_count_inc() - Increment global frag count
+ * @value: Increment value
+ *
+ * Return: none
+ */
+static inline void qdf_frag_count_inc(uint32_t value)
+{
+	return __qdf_frag_count_inc(value);
+}
+
+/**
+ * qdf_frag_count_dec() - Decrement global frag count
+ * @value: Decrement value
+ *
+ * Return: none
+ */
+static inline void qdf_frag_count_dec(uint32_t value)
+{
+	return __qdf_frag_count_dec(value);
+}
+
+/**
+ * qdf_frag_mod_init() - Initialization routine for qdf_frag
+ *
+ * Return: none
+ */
+static inline void qdf_frag_mod_init(void)
+{
+	return __qdf_frag_mod_init();
+}
+
+/**
+ * qdf_frag_mod_exit() - Unintialization routine for qdf_frag
+ *
+ * Return: none
+ */
+static inline void qdf_frag_mod_exit(void)
+{
+	return __qdf_frag_mod_exit();
+}
+
 /**
 /**
  * qdf_mem_map_page() - Map Page
  * qdf_mem_map_page() - Map Page
  * @osdev: qdf_device_t
  * @osdev: qdf_device_t
@@ -67,23 +284,4 @@ static inline void qdf_mem_unmap_page(qdf_device_t osdev, qdf_dma_addr_t paddr,
 	__qdf_mem_unmap_page(osdev, paddr, nbytes, dir);
 	__qdf_mem_unmap_page(osdev, paddr, nbytes, dir);
 }
 }
 
 
-/**
- * qdf_frag_free() - Free allocated frag memory
- * @vaddr: Frag address to be freed.
- */
-static inline void qdf_frag_free(qdf_frag_t vaddr)
-{
-	__qdf_frag_free(vaddr);
-}
-
-/**
- * qdf_frag_alloc() - Allocate frag memory
- * @fragsz: Size of frag memory to be allocated
- *
- * Return: Allcated frag address
- */
-static inline qdf_frag_t qdf_frag_alloc(unsigned int fragsz)
-{
-	return __qdf_frag_alloc(fragsz);
-}
 #endif /* _QDF_NBUF_FRAG_H */
 #endif /* _QDF_NBUF_FRAG_H */

+ 17 - 15
qdf/linux/src/i_qdf_nbuf.h

@@ -1023,6 +1023,17 @@ uint8_t __qdf_nbuf_get_exemption_type(struct sk_buff *skb);
 void __qdf_nbuf_ref(struct sk_buff *skb);
 void __qdf_nbuf_ref(struct sk_buff *skb);
 int __qdf_nbuf_shared(struct sk_buff *skb);
 int __qdf_nbuf_shared(struct sk_buff *skb);
 
 
+/**
+ * __qdf_nbuf_get_nr_frags() - return the number of fragments in an skb,
+ * @skb: sk buff
+ *
+ * Return: number of fragments
+ */
+static inline size_t __qdf_nbuf_get_nr_frags(struct sk_buff *skb)
+{
+	return skb_shinfo(skb)->nr_frags;
+}
+
 /*
 /*
  * qdf_nbuf_pool_delete() implementation - do nothing in linux
  * qdf_nbuf_pool_delete() implementation - do nothing in linux
  */
  */
@@ -1045,9 +1056,10 @@ static inline struct sk_buff *__qdf_nbuf_clone(struct sk_buff *skb)
 	struct sk_buff *skb_new = NULL;
 	struct sk_buff *skb_new = NULL;
 
 
 	skb_new = skb_clone(skb, GFP_ATOMIC);
 	skb_new = skb_clone(skb, GFP_ATOMIC);
-	if (skb_new)
+	if (skb_new) {
+		__qdf_frag_count_inc(__qdf_nbuf_get_nr_frags(skb_new));
 		__qdf_nbuf_count_inc(skb_new);
 		__qdf_nbuf_count_inc(skb_new);
-
+	}
 	return skb_new;
 	return skb_new;
 }
 }
 
 
@@ -1065,9 +1077,10 @@ static inline struct sk_buff *__qdf_nbuf_copy(struct sk_buff *skb)
 	struct sk_buff *skb_new = NULL;
 	struct sk_buff *skb_new = NULL;
 
 
 	skb_new = skb_copy(skb, GFP_ATOMIC);
 	skb_new = skb_copy(skb, GFP_ATOMIC);
-	if (skb_new)
+	if (skb_new) {
+		__qdf_frag_count_inc(__qdf_nbuf_get_nr_frags(skb_new));
 		__qdf_nbuf_count_inc(skb_new);
 		__qdf_nbuf_count_inc(skb_new);
-
+	}
 	return skb_new;
 	return skb_new;
 }
 }
 
 
@@ -2060,17 +2073,6 @@ __qdf_nbuf_headlen(struct sk_buff *skb)
 	return skb_headlen(skb);
 	return skb_headlen(skb);
 }
 }
 
 
-/**
- * __qdf_nbuf_get_nr_frags() - return the number of fragments in an skb,
- * @skb: sk buff
- *
- * Return: number of fragments
- */
-static inline size_t __qdf_nbuf_get_nr_frags(struct sk_buff *skb)
-{
-	return skb_shinfo(skb)->nr_frags;
-}
-
 /**
 /**
  * __qdf_nbuf_tso_tcp_v4() - to check if the TSO TCP pkt is a IPv4 or not.
  * __qdf_nbuf_tso_tcp_v4() - to check if the TSO TCP pkt is a IPv4 or not.
  * @buf: sk buff
  * @buf: sk buff

+ 77 - 2
qdf/linux/src/i_qdf_nbuf_frag.h

@@ -27,11 +27,76 @@
 #include <qdf_net_types.h>
 #include <qdf_net_types.h>
 #include <qdf_mem.h>
 #include <qdf_mem.h>
 
 
+#define QDF_NBUF_FRAG_DEBUG_COUNT_ZERO    0
+#define QDF_NBUF_FRAG_DEBUG_COUNT_ONE     1
+
 /**
 /**
  * typedef __qdf_frag_t - Abstraction for void * for frag address
  * typedef __qdf_frag_t - Abstraction for void * for frag address
  */
  */
 typedef void *__qdf_frag_t;
 typedef void *__qdf_frag_t;
 
 
+#ifdef QDF_NBUF_FRAG_GLOBAL_COUNT
+
+/**
+ * __qdf_frag_count_get() - Get global frag count
+ *
+ * Return: Global frag gauge
+ */
+uint32_t __qdf_frag_count_get(void);
+
+/**
+ * __qdf_frag_count_inc() - Increment frag global count
+ * @value: Increment value
+ *
+ * Return: none
+ */
+void __qdf_frag_count_inc(uint32_t value);
+
+/**
+ * __qdf_frag_count_dec() - Decrement frag global count
+ * @value: Decrement value
+ *
+ * Return: none
+ */
+void __qdf_frag_count_dec(uint32_t value);
+
+/*
+ * __qdf_frag_mod_init() - Initialization routine for qdf_frag
+ *
+ * Return: none
+ */
+void __qdf_frag_mod_init(void);
+
+/**
+ * __qdf_frag_mod_exit() - Uninitialization routine for qdf_frag
+ *
+ * Return: none
+ */
+void __qdf_frag_mod_exit(void);
+
+#else
+static inline uint32_t __qdf_frag_count_get(void)
+{
+	return 0;
+}
+
+static inline void __qdf_frag_count_inc(uint32_t value)
+{
+}
+
+static inline void __qdf_frag_count_dec(uint32_t value)
+{
+}
+
+static inline void __qdf_frag_mod_init(void)
+{
+}
+
+static inline void __qdf_frag_mod_exit(void)
+{
+}
+#endif /* QDF_NBUF_FRAG_GLOBAL_COUNT */
+
 /**
 /**
  * Maximum number of frags an SKB can hold
  * Maximum number of frags an SKB can hold
  */
  */
@@ -64,10 +129,15 @@ QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf,
 /**
 /**
  * __qdf_frag_free() - Free allocated frag memory
  * __qdf_frag_free() - Free allocated frag memory
  * @vaddr: Frag address to be freed
  * @vaddr: Frag address to be freed
+ *
+ * Return: none
  */
  */
 static inline void __qdf_frag_free(__qdf_frag_t vaddr)
 static inline void __qdf_frag_free(__qdf_frag_t vaddr)
 {
 {
-	skb_free_frag(vaddr);
+	if (qdf_likely(vaddr)) {
+		skb_free_frag(vaddr);
+		__qdf_frag_count_dec(QDF_NBUF_FRAG_DEBUG_COUNT_ONE);
+	}
 }
 }
 
 
 /**
 /**
@@ -78,6 +148,11 @@ static inline void __qdf_frag_free(__qdf_frag_t vaddr)
  */
  */
 static inline __qdf_frag_t __qdf_frag_alloc(unsigned int fragsz)
 static inline __qdf_frag_t __qdf_frag_alloc(unsigned int fragsz)
 {
 {
-	return netdev_alloc_frag(fragsz);
+	__qdf_frag_t p_frag = netdev_alloc_frag(fragsz);
+
+	if (p_frag)
+		__qdf_frag_count_inc(QDF_NBUF_FRAG_DEBUG_COUNT_ONE);
+	return p_frag;
 }
 }
+
 #endif /* _I_QDF_NBUF_FRAG_H */
 #endif /* _I_QDF_NBUF_FRAG_H */

+ 2 - 0
qdf/linux/src/qdf_mem.c

@@ -2259,6 +2259,7 @@ void qdf_mem_init(void)
 {
 {
 	qdf_mem_debug_init();
 	qdf_mem_debug_init();
 	qdf_net_buf_debug_init();
 	qdf_net_buf_debug_init();
+	qdf_frag_debug_init();
 	qdf_mem_debugfs_init();
 	qdf_mem_debugfs_init();
 	qdf_mem_debug_debugfs_init();
 	qdf_mem_debug_debugfs_init();
 }
 }
@@ -2268,6 +2269,7 @@ void qdf_mem_exit(void)
 {
 {
 	qdf_mem_debug_debugfs_exit();
 	qdf_mem_debug_debugfs_exit();
 	qdf_mem_debugfs_exit();
 	qdf_mem_debugfs_exit();
+	qdf_frag_debug_exit();
 	qdf_net_buf_debug_exit();
 	qdf_net_buf_debug_exit();
 	qdf_mem_debug_exit();
 	qdf_mem_debug_exit();
 }
 }

+ 2 - 0
qdf/linux/src/qdf_module.c

@@ -55,6 +55,7 @@ int qdf_mod_init(void)
 	qdf_logging_init();
 	qdf_logging_init();
 	qdf_perfmod_init();
 	qdf_perfmod_init();
 	qdf_nbuf_mod_init();
 	qdf_nbuf_mod_init();
+	qdf_frag_mod_init();
 	qdf_event_list_init();
 	qdf_event_list_init();
 
 
 	return 0;
 	return 0;
@@ -75,6 +76,7 @@ void qdf_mod_exit(void)
 #endif
 #endif
 {
 {
 	qdf_event_list_destroy();
 	qdf_event_list_destroy();
+	qdf_frag_mod_exit();
 	qdf_nbuf_mod_exit();
 	qdf_nbuf_mod_exit();
 	qdf_perfmod_exit();
 	qdf_perfmod_exit();
 	qdf_logging_exit();
 	qdf_logging_exit();

+ 139 - 1
qdf/linux/src/qdf_nbuf.c

@@ -581,6 +581,7 @@ void __qdf_nbuf_free(struct sk_buff *skb)
 	if (pld_nbuf_pre_alloc_free(skb))
 	if (pld_nbuf_pre_alloc_free(skb))
 		return;
 		return;
 
 
+	qdf_frag_count_dec(qdf_nbuf_get_nr_frags(skb));
 	qdf_nbuf_count_dec(skb);
 	qdf_nbuf_count_dec(skb);
 	qdf_mem_skb_dec(skb->truesize);
 	qdf_mem_skb_dec(skb->truesize);
 	if (nbuf_free_cb)
 	if (nbuf_free_cb)
@@ -2811,6 +2812,9 @@ qdf_export_symbol(qdf_nbuf_alloc_no_recycler_debug);
 void qdf_nbuf_free_debug(qdf_nbuf_t nbuf, const char *func, uint32_t line)
 void qdf_nbuf_free_debug(qdf_nbuf_t nbuf, const char *func, uint32_t line)
 {
 {
 	qdf_nbuf_t ext_list;
 	qdf_nbuf_t ext_list;
+	qdf_frag_t p_frag;
+	uint32_t num_nr_frags;
+	uint32_t idx = 0;
 
 
 	if (qdf_unlikely(!nbuf))
 	if (qdf_unlikely(!nbuf))
 		return;
 		return;
@@ -2826,6 +2830,18 @@ void qdf_nbuf_free_debug(qdf_nbuf_t nbuf, const char *func, uint32_t line)
 	qdf_net_buf_debug_delete_node(nbuf);
 	qdf_net_buf_debug_delete_node(nbuf);
 	qdf_nbuf_history_add(nbuf, func, line, QDF_NBUF_FREE);
 	qdf_nbuf_history_add(nbuf, func, line, QDF_NBUF_FREE);
 
 
+	/* Take care to delete the debug entries for frags */
+	num_nr_frags = qdf_nbuf_get_nr_frags(nbuf);
+
+	qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS);
+
+	while (idx < num_nr_frags) {
+		p_frag = qdf_nbuf_get_frag_addr(nbuf, idx);
+		if (qdf_likely(p_frag))
+			qdf_frag_debug_refcount_dec(p_frag, func, line);
+		idx++;
+	}
+
 	/* Take care to delete the debug entries for frag_list */
 	/* Take care to delete the debug entries for frag_list */
 	ext_list = qdf_nbuf_get_ext_list(nbuf);
 	ext_list = qdf_nbuf_get_ext_list(nbuf);
 	while (ext_list) {
 	while (ext_list) {
@@ -2844,6 +2860,10 @@ qdf_export_symbol(qdf_nbuf_free_debug);
 
 
 qdf_nbuf_t qdf_nbuf_clone_debug(qdf_nbuf_t buf, const char *func, uint32_t line)
 qdf_nbuf_t qdf_nbuf_clone_debug(qdf_nbuf_t buf, const char *func, uint32_t line)
 {
 {
+	uint32_t num_nr_frags;
+	uint32_t idx = 0;
+	qdf_frag_t p_frag;
+
 	qdf_nbuf_t cloned_buf = __qdf_nbuf_clone(buf);
 	qdf_nbuf_t cloned_buf = __qdf_nbuf_clone(buf);
 
 
 	if (is_initial_mem_debug_disabled)
 	if (is_initial_mem_debug_disabled)
@@ -2852,6 +2872,18 @@ qdf_nbuf_t qdf_nbuf_clone_debug(qdf_nbuf_t buf, const char *func, uint32_t line)
 	if (qdf_unlikely(!cloned_buf))
 	if (qdf_unlikely(!cloned_buf))
 		return NULL;
 		return NULL;
 
 
+	/* Take care to update the debug entries for frags */
+	num_nr_frags = qdf_nbuf_get_nr_frags(cloned_buf);
+
+	qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS);
+
+	while (idx < num_nr_frags) {
+		p_frag = qdf_nbuf_get_frag_addr(cloned_buf, idx);
+		if (qdf_likely(p_frag))
+			qdf_frag_debug_refcount_inc(p_frag, func, line);
+		idx++;
+	}
+
 	/* Store SKB in internal QDF tracking table */
 	/* Store SKB in internal QDF tracking table */
 	qdf_net_buf_debug_add_node(cloned_buf, 0, func, line);
 	qdf_net_buf_debug_add_node(cloned_buf, 0, func, line);
 	qdf_nbuf_history_add(cloned_buf, func, line, QDF_NBUF_ALLOC_CLONE);
 	qdf_nbuf_history_add(cloned_buf, func, line, QDF_NBUF_ALLOC_CLONE);
@@ -4691,8 +4723,114 @@ void __qdf_nbuf_add_rx_frag(__qdf_frag_t buf, __qdf_nbuf_t nbuf,
 			(frag_offset + offset),
 			(frag_offset + offset),
 			frag_len, truesize);
 			frag_len, truesize);
 
 
-	if (unlikely(take_frag_ref))
+	if (unlikely(take_frag_ref)) {
+		qdf_frag_count_inc(QDF_NBUF_FRAG_DEBUG_COUNT_ONE);
 		skb_frag_ref(nbuf, nr_frag);
 		skb_frag_ref(nbuf, nr_frag);
+	}
 }
 }
 
 
 qdf_export_symbol(__qdf_nbuf_add_rx_frag);
 qdf_export_symbol(__qdf_nbuf_add_rx_frag);
+
+#ifdef NBUF_FRAG_MEMORY_DEBUG
+
+QDF_STATUS qdf_nbuf_move_frag_page_offset_debug(qdf_nbuf_t nbuf, uint8_t idx,
+						int offset, const char *func,
+						uint32_t line)
+{
+	QDF_STATUS result;
+	qdf_frag_t p_fragp, n_fragp;
+
+	p_fragp = qdf_nbuf_get_frag_addr(nbuf, idx);
+	result = __qdf_nbuf_move_frag_page_offset(nbuf, idx, offset);
+
+	n_fragp = qdf_nbuf_get_frag_addr(nbuf, idx);
+
+	/*
+	 * Update frag address in frag debug tracker
+	 * when frag offset is successfully changed in skb
+	 */
+	if (result == QDF_STATUS_SUCCESS)
+		qdf_frag_debug_update_addr(p_fragp, n_fragp, func, line);
+
+	return result;
+}
+
+qdf_export_symbol(qdf_nbuf_move_frag_page_offset_debug);
+
+void qdf_nbuf_add_rx_frag_debug(qdf_frag_t buf, qdf_nbuf_t nbuf,
+				int offset, int frag_len,
+				unsigned int truesize, bool take_frag_ref,
+				const char *func, uint32_t line)
+{
+	qdf_frag_t fragp;
+	uint32_t num_nr_frags;
+
+	__qdf_nbuf_add_rx_frag(buf, nbuf, offset,
+			       frag_len, truesize, take_frag_ref);
+
+	num_nr_frags = qdf_nbuf_get_nr_frags(nbuf);
+
+	qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS);
+
+	fragp = qdf_nbuf_get_frag_addr(nbuf, num_nr_frags - 1);
+
+	/* Update frag address in frag debug tracking table */
+	if (fragp != buf)
+		qdf_frag_debug_update_addr(buf, fragp, func, line);
+
+	/* Update frag refcount in frag debug tracking table */
+	qdf_frag_debug_refcount_inc(fragp, func, line);
+}
+
+qdf_export_symbol(qdf_nbuf_add_rx_frag_debug);
+
+void qdf_net_buf_debug_acquire_frag(qdf_nbuf_t buf, const char *func,
+				    uint32_t line)
+{
+	uint32_t num_nr_frags;
+	uint32_t idx = 0;
+	qdf_frag_t p_frag;
+
+	if (qdf_unlikely(!buf))
+		return;
+
+	/* Take care to update the refcount in the debug entries for frags */
+	num_nr_frags = qdf_nbuf_get_nr_frags(buf);
+
+	qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS);
+
+	while (idx < num_nr_frags) {
+		p_frag = qdf_nbuf_get_frag_addr(buf, idx);
+		if (qdf_likely(p_frag))
+			qdf_frag_debug_refcount_inc(p_frag, func, line);
+		idx++;
+	}
+}
+
+qdf_export_symbol(qdf_net_buf_debug_acquire_frag);
+
+void qdf_net_buf_debug_release_frag(qdf_nbuf_t buf, const char *func,
+				    uint32_t line)
+{
+	uint32_t num_nr_frags;
+	uint32_t idx = 0;
+	qdf_frag_t p_frag;
+
+	if (qdf_unlikely(!buf))
+		return;
+
+	/* Take care to update the refcount in the debug entries for frags */
+	num_nr_frags = qdf_nbuf_get_nr_frags(buf);
+
+	qdf_assert_always(num_nr_frags <= QDF_NBUF_MAX_FRAGS);
+
+	while (idx < num_nr_frags) {
+		p_frag = qdf_nbuf_get_frag_addr(buf, idx);
+		if (qdf_likely(p_frag))
+			qdf_frag_debug_refcount_dec(p_frag, func, line);
+		idx++;
+	}
+}
+
+qdf_export_symbol(qdf_net_buf_debug_release_frag);
+#endif /* NBUF_FRAG_MEMORY_DEBUG */

+ 758 - 0
qdf/linux/src/qdf_nbuf_frag.c

@@ -21,8 +21,766 @@
  * QCA driver framework(QDF) network nbuf frag management APIs
  * QCA driver framework(QDF) network nbuf frag management APIs
  */
  */
 
 
+#include <qdf_atomic.h>
+#include <qdf_list.h>
+#include <qdf_debugfs.h>
 #include <qdf_module.h>
 #include <qdf_module.h>
 #include <qdf_nbuf_frag.h>
 #include <qdf_nbuf_frag.h>
+#include <qdf_trace.h>
+#include "qdf_str.h"
+
+#ifdef QDF_NBUF_FRAG_GLOBAL_COUNT
+#define FRAG_DEBUGFS_NAME    "frag_counters"
+static qdf_atomic_t frag_count;
+#endif
+
+#if defined(NBUF_FRAG_MEMORY_DEBUG) || defined(QDF_NBUF_FRAG_GLOBAL_COUNT)
+static bool is_initial_mem_debug_disabled;
+#endif
+
+#ifdef QDF_NBUF_FRAG_GLOBAL_COUNT
+
+uint32_t __qdf_frag_count_get(void)
+{
+	return qdf_atomic_read(&frag_count);
+}
+
+qdf_export_symbol(__qdf_frag_count_get);
+
+void __qdf_frag_count_inc(uint32_t value)
+{
+	if (qdf_likely(is_initial_mem_debug_disabled))
+		return;
+
+	qdf_atomic_add(value, &frag_count);
+}
+
+qdf_export_symbol(__qdf_frag_count_inc);
+
+void __qdf_frag_count_dec(uint32_t value)
+{
+	if (qdf_likely(is_initial_mem_debug_disabled))
+		return;
+
+	qdf_atomic_sub(value, &frag_count);
+}
+
+qdf_export_symbol(__qdf_frag_count_dec);
+
+void __qdf_frag_mod_init(void)
+{
+	is_initial_mem_debug_disabled = qdf_mem_debug_config_get();
+	qdf_atomic_init(&frag_count);
+	qdf_debugfs_create_atomic(FRAG_DEBUGFS_NAME, S_IRUSR, NULL,
+				  &frag_count);
+}
+
+void __qdf_frag_mod_exit(void)
+{
+}
+#endif /* QDF_NBUF_FRAG_GLOBAL_COUNT */
+
+#ifdef NBUF_FRAG_MEMORY_DEBUG
+
+#define QDF_FRAG_TRACK_MAX_SIZE    1024
+
+/**
+ * struct qdf_frag_track_node_t - Network frag tracking node structure
+ * @hnode: list_head for next and prev pointers
+ * @p_frag: Pointer to frag
+ * @alloc_func_name: Function where frag is allocated
+ * @alloc_func_line: Allocation function line no.
+ * @refcount: No. of refereces to the frag
+ * @last_func_name: Function where frag recently accessed
+ * @last_func_line_num: Line number of last function
+ *
+ **/
+struct qdf_frag_track_node_t {
+	qdf_list_node_t hnode;
+	qdf_frag_t p_frag;
+	char alloc_func_name[QDF_MEM_FUNC_NAME_SIZE];
+	uint32_t alloc_func_line;
+	uint8_t refcount;
+	char last_func_name[QDF_MEM_FUNC_NAME_SIZE];
+	uint32_t last_func_line;
+};
+
+/**
+ * struct qdf_frag_tracking_list_t - Frag node tracking list
+ * @track_list: qdf_list_t for maintaining the list
+ * @list_lock: Lock over the list
+ *
+ */
+typedef struct qdf_frag_tracking_list_t {
+	qdf_list_t track_list;
+	qdf_spinlock_t list_lock;
+} qdf_frag_tracking_list;
+
+typedef struct qdf_frag_track_node_t QDF_FRAG_TRACK;
+
+/**
+ * Array of tracking list for maintaining
+ * allocated debug frag nodes as per the calculated
+ * hash value.
+ */
+static qdf_frag_tracking_list gp_qdf_frag_track_tbl[QDF_FRAG_TRACK_MAX_SIZE];
+
+static struct kmem_cache *frag_tracking_cache;
+
+/* Tracking list for maintaining the free debug frag nodes */
+static qdf_frag_tracking_list qdf_frag_track_free_list;
+
+/**
+ * Parameters for statistics
+ * qdf_frag_track_free_list_count: No. of free nodes
+ * qdf_frag_track_used_list_count : No. of nodes used
+ * qdf_frag_track_max_used : Max no. of nodes used during execution
+ * qdf_frag_track_max_free : Max free nodes observed during execution
+ * qdf_frag_track_max_allocated: Max no. of allocated nodes
+ */
+static uint32_t qdf_frag_track_free_list_count;
+static uint32_t qdf_frag_track_used_list_count;
+static uint32_t qdf_frag_track_max_used;
+static uint32_t qdf_frag_track_max_free;
+static uint32_t qdf_frag_track_max_allocated;
+
+/**
+ * qdf_frag_update_max_used() - Update qdf_frag_track_max_used tracking variable
+ *
+ * Tracks the max number of frags that the wlan driver was tracking at any one
+ * time
+ *
+ * Return: none
+ **/
+static inline void qdf_frag_update_max_used(void)
+{
+	int sum;
+
+	/* Update max_used if it is less than used list count */
+	if (qdf_frag_track_max_used < qdf_frag_track_used_list_count)
+		qdf_frag_track_max_used = qdf_frag_track_used_list_count;
+
+	/* Calculate no. of allocated nodes */
+	sum = qdf_frag_track_used_list_count + qdf_frag_track_free_list_count;
+
+	/* Update max allocated if less then no. of allocated nodes */
+	if (qdf_frag_track_max_allocated < sum)
+		qdf_frag_track_max_allocated = sum;
+}
+
+/**
+ * qdf_frag_update_max_free() - Update qdf_frag_track_max_free
+ *
+ * Tracks the max number tracking buffers kept in the freelist.
+ *
+ * Return: none
+ */
+static inline void qdf_frag_update_max_free(void)
+{
+	if (qdf_frag_track_max_free < qdf_frag_track_free_list_count)
+		qdf_frag_track_max_free = qdf_frag_track_free_list_count;
+}
+
+/**
+ * qdf_frag_track_alloc() - Allocate a cookie to track frags allocated by wlan
+ *
+ * This function pulls from freelist if possible,otherwise uses kmem_cache_alloc
+ * This function also adds fexibility to adjust the allocation and freelist
+ * schemes.
+ *
+ * Return: Pointer to an unused QDF_FRAG_TRACK structure which may not be zeroed
+ */
+static QDF_FRAG_TRACK *qdf_frag_track_alloc(void)
+{
+	int flags = GFP_KERNEL;
+	QDF_FRAG_TRACK *frag_track_node = NULL;
+	qdf_list_node_t *temp_list_node;
+
+	qdf_spin_lock_irqsave(&qdf_frag_track_free_list.list_lock);
+	qdf_frag_track_used_list_count++;
+
+	if (!qdf_list_empty(&qdf_frag_track_free_list.track_list)) {
+		qdf_list_remove_front(&qdf_frag_track_free_list.track_list,
+				      &temp_list_node);
+		frag_track_node = qdf_container_of(temp_list_node,
+						   struct qdf_frag_track_node_t,
+						   hnode);
+		qdf_frag_track_free_list_count--;
+	}
+
+	qdf_frag_update_max_used();
+	qdf_spin_unlock_irqrestore(&qdf_frag_track_free_list.list_lock);
+
+	if (frag_track_node)
+		return frag_track_node;
+
+	if (in_interrupt() || irqs_disabled() || in_atomic())
+		flags = GFP_ATOMIC;
+
+	frag_track_node = kmem_cache_alloc(frag_tracking_cache, flags);
+	if (frag_track_node)
+		qdf_init_list_head(&frag_track_node->hnode);
+
+	return frag_track_node;
+}
+
+/* FREEQ_POOLSIZE initial and minimum desired freelist poolsize */
+#define FREEQ_POOLSIZE    2048
+
+/**
+ * qdf_frag_track_free() - Free the frag tracking cookie.
+ * @frag_track_node : Debug frag node address
+ *
+ * Matches calls to qdf_frag_track_alloc.
+ * Either frees the tracking cookie to kernel or an internal
+ * freelist based on the size of the freelist.
+ *
+ * Return: none
+ */
+static void qdf_frag_track_free(QDF_FRAG_TRACK *frag_track_node)
+{
+	if (!frag_track_node)
+		return;
+
+	/*
+	 * Try to shrink the freelist if free_list_count > than FREEQ_POOLSIZE
+	 * only shrink the freelist if it is bigger than twice the number of
+	 * frags in use. Otherwise add the frag debug track node to the front
+	 * of qdf_frag_track_free_list.
+	 */
+
+	qdf_spin_lock_irqsave(&qdf_frag_track_free_list.list_lock);
+
+	qdf_frag_track_used_list_count--;
+	if (qdf_frag_track_free_list_count > FREEQ_POOLSIZE &&
+	    (qdf_frag_track_free_list_count >
+	    qdf_frag_track_used_list_count << 1)) {
+		kmem_cache_free(frag_tracking_cache, frag_track_node);
+	} else {
+		qdf_list_insert_front(&qdf_frag_track_free_list.track_list,
+				      &frag_track_node->hnode);
+		qdf_frag_track_free_list_count++;
+	}
+	qdf_frag_update_max_free();
+	qdf_spin_unlock_irqrestore(&qdf_frag_track_free_list.list_lock);
+}
+
+/**
+ * qdf_frag_track_prefill() - Prefill the frag tracking cookie freelist
+ *
+ * Return: none
+ */
+static void qdf_frag_track_prefill(void)
+{
+	int index;
+	QDF_FRAG_TRACK *curr_node, *next_node;
+	qdf_list_t temp_list;
+
+	qdf_list_create(&temp_list, 0);
+
+	/* Prepopulate the freelist */
+	for (index = 0; index < FREEQ_POOLSIZE; index++) {
+		curr_node = qdf_frag_track_alloc();
+		if (!curr_node)
+			continue;
+		qdf_list_insert_front(&temp_list, &curr_node->hnode);
+	}
+
+	curr_node = NULL;
+	next_node = NULL;
+
+	qdf_list_for_each_del(&temp_list, curr_node, next_node, hnode) {
+		qdf_list_remove_node(&temp_list, &curr_node->hnode);
+		qdf_frag_track_free(curr_node);
+	}
+
+	/* prefilled buffers should not count as used */
+	qdf_frag_track_max_used = 0;
+
+	qdf_list_destroy(&temp_list);
+}
+
+/**
+ * qdf_frag_track_memory_manager_create() - Manager for frag tracking cookies
+ *
+ * This initializes the memory manager for the frag tracking cookies. Because
+ * these cookies are all the same size and only used in this feature, we can
+ * use a kmem_cache to provide tracking as well as to speed up allocations.
+ * To avoid the overhead of allocating and freeing the buffers (including SLUB
+ * features) a freelist is prepopulated here.
+ *
+ * Return: none
+ */
+static void qdf_frag_track_memory_manager_create(void)
+{
+	qdf_spinlock_create(&qdf_frag_track_free_list.list_lock);
+	qdf_list_create(&qdf_frag_track_free_list.track_list, 0);
+	frag_tracking_cache = kmem_cache_create("qdf_frag_tracking_cache",
+						sizeof(QDF_FRAG_TRACK),
+						0, 0, NULL);
+
+	qdf_frag_track_prefill();
+}
+
+/**
+ * qdf_frag_track_memory_manager_destroy() - Manager for frag tracking cookies
+ *
+ * Empty the freelist and print out usage statistics when it is no longer
+ * needed. Also the kmem_cache should be destroyed here so that it can warn if
+ * any frag tracking cookies were leaked.
+ *
+ * Return: none
+ */
+static void qdf_frag_track_memory_manager_destroy(void)
+{
+	QDF_FRAG_TRACK *curr_node, *next_node;
+
+	curr_node = next_node = NULL;
+
+	qdf_spin_lock_irqsave(&qdf_frag_track_free_list.list_lock);
+
+	if (qdf_frag_track_max_used > FREEQ_POOLSIZE * 4)
+		qdf_info("Unexpectedly large max_used count %d",
+			  qdf_frag_track_max_used);
+
+	if (qdf_frag_track_max_used < qdf_frag_track_max_allocated)
+		qdf_info("%d Unused trackers were allocated",
+			  qdf_frag_track_max_allocated -
+			  qdf_frag_track_max_used);
+
+	if (qdf_frag_track_free_list_count > FREEQ_POOLSIZE &&
+	    qdf_frag_track_free_list_count > 3 * qdf_frag_track_max_used / 4)
+		qdf_info("Check freelist shrinking functionality");
+
+	qdf_info("%d Residual freelist size", qdf_frag_track_free_list_count);
+
+	qdf_info("%d Max freelist size observed", qdf_frag_track_max_free);
+
+	qdf_info("%d Max buffers used observed", qdf_frag_track_max_used);
+
+	qdf_info("%d Max buffers allocated observed",
+		  qdf_frag_track_max_allocated);
+
+	qdf_list_for_each_del(&qdf_frag_track_free_list.track_list,
+			      curr_node, next_node, hnode) {
+		qdf_list_remove_node(&qdf_frag_track_free_list.track_list,
+				     &curr_node->hnode);
+		kmem_cache_free(frag_tracking_cache, curr_node);
+		qdf_frag_track_free_list_count--;
+	}
+
+	if (qdf_frag_track_free_list_count != 0)
+		qdf_info("%d Unfreed tracking memory lost in freelist",
+			 qdf_frag_track_free_list_count);
+
+	if (qdf_frag_track_used_list_count != 0)
+		qdf_info("%d Unfreed tracking memory still in use",
+			 qdf_frag_track_used_list_count);
+
+	qdf_spin_unlock_irqrestore(&qdf_frag_track_free_list.list_lock);
+	kmem_cache_destroy(frag_tracking_cache);
+
+	qdf_list_destroy(&qdf_frag_track_free_list.track_list);
+	qdf_spinlock_destroy(&qdf_frag_track_free_list.list_lock);
+}
+
+/**
+ * qdf_frag_debug_init() - Initialize network frag debug functionality
+ *
+ * QDF frag buffer debug feature tracks all frags allocated by WLAN driver
+ * in a hash table and when driver is unloaded it reports about leaked frags.
+ *
+ * Return: none
+ */
+void qdf_frag_debug_init(void)
+{
+	uint32_t index;
+
+	is_initial_mem_debug_disabled = qdf_mem_debug_config_get();
+
+	if (is_initial_mem_debug_disabled)
+		return;
+
+	qdf_frag_track_memory_manager_create();
+
+	for (index = 0; index < QDF_FRAG_TRACK_MAX_SIZE; index++) {
+		qdf_list_create(&gp_qdf_frag_track_tbl[index].track_list, 0);
+		qdf_spinlock_create(&gp_qdf_frag_track_tbl[index].list_lock);
+	}
+}
+
+qdf_export_symbol(qdf_frag_debug_init);
+
+/**
+ * qdf_frag_buf_debug_exit() - Exit network frag debug functionality
+ *
+ * Exit network frag tracking debug functionality and log frag memory leaks
+ *
+ * Return: none
+ */
+void qdf_frag_debug_exit(void)
+{
+	uint32_t index;
+	QDF_FRAG_TRACK *p_node;
+	QDF_FRAG_TRACK *p_prev;
+
+	if (is_initial_mem_debug_disabled)
+		return;
+
+	for (index = 0; index < QDF_FRAG_TRACK_MAX_SIZE; index++) {
+		qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
+		qdf_list_for_each_del(&gp_qdf_frag_track_tbl[index].track_list,
+				      p_prev, p_node, hnode) {
+			qdf_list_remove_node(
+				&gp_qdf_frag_track_tbl[index].track_list,
+				&p_prev->hnode);
+			qdf_info("******Frag Memory Leak******");
+			qdf_info("@Frag Address: %pK", p_prev->p_frag);
+			qdf_info("@Refcount: %u", p_prev->refcount);
+			qdf_info("@Alloc Func Name: %s, @Alloc Func Line: %d",
+				 p_prev->alloc_func_name,
+				 p_prev->alloc_func_line);
+			qdf_info("@Last Func Name: %s, @Last Func Line: %d",
+				 p_prev->last_func_name,
+				 p_prev->last_func_line);
+			qdf_info("****************************");
+
+			qdf_frag_track_free(p_prev);
+		}
+		qdf_list_destroy(&gp_qdf_frag_track_tbl[index].track_list);
+		qdf_spin_unlock_irqrestore(
+				&gp_qdf_frag_track_tbl[index].list_lock);
+		qdf_spinlock_destroy(&gp_qdf_frag_track_tbl[index].list_lock);
+	}
+
+	qdf_frag_track_memory_manager_destroy();
+}
+
+qdf_export_symbol(qdf_frag_debug_exit);
+
+/**
+ * qdf_frag_debug_hash() - Hash network frag pointer
+ * @p_frag: Frag address
+ *
+ * Return: hash value
+ */
+static uint32_t qdf_frag_debug_hash(qdf_frag_t p_frag)
+{
+	uint32_t index;
+
+	index = (uint32_t)(((uintptr_t)p_frag) >> 4);
+	index += (uint32_t)(((uintptr_t)p_frag) >> 14);
+	index &= (QDF_FRAG_TRACK_MAX_SIZE - 1);
+
+	return index;
+}
+
+/**
+ * qdf_frag_debug_look_up() - Look up network frag in debug hash table
+ * @p_frag: Frag address
+ *
+ * Return: If frag is found in hash table then return pointer to network frag
+ *	else return NULL
+ */
+static QDF_FRAG_TRACK *qdf_frag_debug_look_up(qdf_frag_t p_frag)
+{
+	uint32_t index;
+	QDF_FRAG_TRACK *p_node;
+
+	index = qdf_frag_debug_hash(p_frag);
+
+	qdf_list_for_each(&gp_qdf_frag_track_tbl[index].track_list, p_node,
+			  hnode) {
+		if (p_node->p_frag == p_frag)
+			return p_node;
+	}
+
+	return NULL;
+}
+
+/**
+ * __qdf_frag_debug_add_node()- Add frag node to debug tracker
+ * @fragp: Frag Pointer
+ * @idx: Index
+ * @func_name: Caller function name
+ * @line_num: Caller function line no.
+ *
+ * Return: Allocated frag tracker node address
+ */
+static QDF_FRAG_TRACK *__qdf_frag_debug_add_node(qdf_frag_t fragp,
+						 uint32_t idx,
+						 const char *func_name,
+						 uint32_t line_num)
+{
+	QDF_FRAG_TRACK *p_node;
+
+	p_node = qdf_frag_track_alloc();
+
+	if (p_node) {
+		p_node->p_frag = fragp;
+		qdf_str_lcopy(p_node->alloc_func_name, func_name,
+			      QDF_MEM_FUNC_NAME_SIZE);
+		p_node->alloc_func_line = line_num;
+		p_node->refcount = QDF_NBUF_FRAG_DEBUG_COUNT_ZERO;
+
+		qdf_str_lcopy(p_node->last_func_name, func_name,
+			      QDF_MEM_FUNC_NAME_SIZE);
+		p_node->last_func_line = line_num;
+
+		qdf_list_insert_front(&gp_qdf_frag_track_tbl[idx].track_list,
+				      &p_node->hnode);
+	}
+	return p_node;
+}
+
+/**
+ * __qdf_frag_debug_delete_node()- Remove frag node from debug tracker
+ * @p_node: Frag node address in debug tracker
+ * @idx: Index
+ *
+ * Return: none
+ */
+static void __qdf_frag_debug_delete_node(QDF_FRAG_TRACK *p_node, uint32_t idx)
+{
+	if (idx < QDF_FRAG_TRACK_MAX_SIZE) {
+		qdf_list_remove_node(&gp_qdf_frag_track_tbl[idx].track_list,
+				     &p_node->hnode);
+		qdf_frag_track_free(p_node);
+	} else {
+		qdf_info("Index value exceeds %d for delete node operation",
+			  QDF_FRAG_TRACK_MAX_SIZE);
+	}
+}
+
+void qdf_frag_debug_add_node(qdf_frag_t fragp, const char *func_name,
+			     uint32_t line_num)
+{
+	uint32_t index;
+	QDF_FRAG_TRACK *p_node;
+
+	if (is_initial_mem_debug_disabled)
+		return;
+
+	index = qdf_frag_debug_hash(fragp);
+
+	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
+
+	p_node = qdf_frag_debug_look_up(fragp);
+
+	if (p_node) {
+		qdf_info("Double addition of frag %pK to debug tracker!!",
+			 fragp);
+		qdf_info("Already added from %s %d Current addition from %s %d",
+			  p_node->alloc_func_name,
+			  p_node->alloc_func_line, func_name, line_num);
+	} else {
+		p_node = __qdf_frag_debug_add_node(fragp, index, func_name,
+						   line_num);
+		if (!p_node)
+			qdf_info("Memory allocation failed !! "
+				 "Add node oprt failed for frag %pK from %s %d",
+				 fragp, func_name, line_num);
+	}
+	qdf_spin_unlock_irqrestore(&gp_qdf_frag_track_tbl[index].list_lock);
+}
+
+void qdf_frag_debug_refcount_inc(qdf_frag_t fragp, const char *func_name,
+				 uint32_t line_num)
+{
+	uint32_t index;
+	QDF_FRAG_TRACK *p_node;
+
+	if (is_initial_mem_debug_disabled)
+		return;
+
+	index = qdf_frag_debug_hash(fragp);
+
+	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
+
+	p_node = qdf_frag_debug_look_up(fragp);
+
+	if (p_node) {
+		(p_node->refcount)++;
+
+		qdf_str_lcopy(p_node->last_func_name, func_name,
+			      QDF_MEM_FUNC_NAME_SIZE);
+		p_node->last_func_line = line_num;
+	} else {
+		p_node = __qdf_frag_debug_add_node(fragp, index, func_name,
+						   line_num);
+		if (p_node)
+			p_node->refcount = QDF_NBUF_FRAG_DEBUG_COUNT_ONE;
+		else
+			qdf_info("Memory allocation failed !! "
+				 "Refcount inc failed for frag %pK from %s %d",
+				 fragp, func_name, line_num);
+	}
+	qdf_spin_unlock_irqrestore(&gp_qdf_frag_track_tbl[index].list_lock);
+}
+
+void qdf_frag_debug_refcount_dec(qdf_frag_t fragp, const char *func_name,
+				 uint32_t line_num)
+{
+	uint32_t index;
+	QDF_FRAG_TRACK *p_node;
+
+	if (is_initial_mem_debug_disabled)
+		return;
+
+	index = qdf_frag_debug_hash(fragp);
+
+	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
+
+	p_node = qdf_frag_debug_look_up(fragp);
+
+	if (p_node) {
+		if (!(p_node->refcount)) {
+			qdf_info("Refcount dec oprt for frag %pK not permitted "
+				 "as refcount=0", fragp);
+			goto done;
+		}
+		(p_node->refcount)--;
+
+		if (!(p_node->refcount)) {
+			/* Remove frag debug node when refcount reaches 0 */
+			__qdf_frag_debug_delete_node(p_node, index);
+		} else {
+			qdf_str_lcopy(p_node->last_func_name, func_name,
+				      QDF_MEM_FUNC_NAME_SIZE);
+			p_node->last_func_line = line_num;
+		}
+	} else {
+		qdf_info("Unallocated frag !! Could not track frag %pK", fragp);
+		qdf_info("Refcount dec oprt failed for frag %pK from %s %d",
+			 fragp, func_name, line_num);
+	}
+done:
+	qdf_spin_unlock_irqrestore(&gp_qdf_frag_track_tbl[index].list_lock);
+}
+
+void qdf_frag_debug_delete_node(qdf_frag_t fragp, const char *func_name,
+				uint32_t line_num)
+{
+	uint32_t index;
+	QDF_FRAG_TRACK *p_node;
+
+	if (is_initial_mem_debug_disabled)
+		return;
+
+	index = qdf_frag_debug_hash(fragp);
+
+	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
+
+	p_node = qdf_frag_debug_look_up(fragp);
+
+	if (p_node) {
+		if (p_node->refcount) {
+			qdf_info("Frag %pK has refcount %d", fragp,
+				 p_node->refcount);
+			qdf_info("Delete oprt failed for frag %pK from %s %d",
+				 fragp, func_name, line_num);
+		} else {
+			/* Remove node from tracker as refcount=0 */
+			__qdf_frag_debug_delete_node(p_node, index);
+		}
+	} else {
+		qdf_info("Unallocated frag !! Double free of frag %pK", fragp);
+		qdf_info("Could not track frag %pK for delete oprt from %s %d",
+			 fragp, func_name, line_num);
+	}
+
+	qdf_spin_unlock_irqrestore(&gp_qdf_frag_track_tbl[index].list_lock);
+}
+
+void qdf_frag_debug_update_addr(qdf_frag_t p_fragp, qdf_frag_t n_fragp,
+				const char *func_name, uint32_t line_num)
+{
+	uint32_t prev_index, new_index;
+	QDF_FRAG_TRACK *p_node;
+
+	prev_index = qdf_frag_debug_hash(p_fragp);
+
+	new_index = qdf_frag_debug_hash(n_fragp);
+
+	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[prev_index].list_lock);
+
+	p_node = qdf_frag_debug_look_up(p_fragp);
+
+	if (!p_node) {
+		qdf_info("Unallocated frag !! Could not track frag %pK",
+			 p_fragp);
+		qdf_info("Update address oprt failed for frag %pK from %s %d",
+			 p_fragp, func_name, line_num);
+		qdf_spin_unlock_irqrestore(
+				&gp_qdf_frag_track_tbl[prev_index].list_lock);
+	} else {
+		/* Update frag address */
+		p_node->p_frag = n_fragp;
+
+		qdf_str_lcopy(p_node->last_func_name, func_name,
+			      QDF_MEM_FUNC_NAME_SIZE);
+		p_node->last_func_line = line_num;
+
+		if (prev_index != new_index) {
+			qdf_list_remove_node(
+				&gp_qdf_frag_track_tbl[prev_index].track_list,
+				&p_node->hnode);
+
+			qdf_spin_unlock_irqrestore(
+				&gp_qdf_frag_track_tbl[prev_index].list_lock);
+
+			qdf_spin_lock_irqsave(
+				&gp_qdf_frag_track_tbl[new_index].list_lock);
+
+			qdf_list_insert_front(
+				&gp_qdf_frag_track_tbl[new_index].track_list,
+				&p_node->hnode);
+
+			qdf_spin_unlock_irqrestore(
+				&gp_qdf_frag_track_tbl[new_index].list_lock);
+		} else {
+			qdf_spin_unlock_irqrestore(
+				&gp_qdf_frag_track_tbl[prev_index].list_lock);
+		}
+	}
+}
+
+qdf_frag_t qdf_frag_alloc_debug(unsigned int frag_size, const char *func_name,
+				uint32_t line_num)
+{
+	qdf_frag_t p_frag;
+
+	if (is_initial_mem_debug_disabled)
+		return __qdf_frag_alloc(frag_size);
+
+	p_frag =  __qdf_frag_alloc(frag_size);
+
+	/* Store frag in QDF Frag Tracking Table */
+	if (qdf_likely(p_frag))
+		qdf_frag_debug_add_node(p_frag, func_name, line_num);
+
+	return p_frag;
+}
+
+qdf_export_symbol(qdf_frag_alloc_debug);
+
+void qdf_frag_free_debug(qdf_frag_t vaddr, const char *func_name,
+			 uint32_t line_num)
+{
+	if (qdf_unlikely(!vaddr))
+		return;
+
+	if (is_initial_mem_debug_disabled)
+		goto free_frag;
+
+	qdf_frag_debug_delete_node(vaddr, func_name, line_num);
+free_frag:
+	__qdf_frag_free(vaddr);
+}
+
+qdf_export_symbol(qdf_frag_free_debug);
+
+#endif /* NBUF_FRAG_MEMORY_DEBUG */
 
 
 #if defined(HIF_PCI)
 #if defined(HIF_PCI)
 QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf,
 QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf,