From d47e448d58ada081e2ba97c5f0c9b7b3abe49071 Mon Sep 17 00:00:00 2001 From: Harsh Kumar Bijlani Date: Mon, 13 Jul 2020 20:02:31 +0530 Subject: [PATCH] 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 --- qdf/inc/qdf_nbuf.h | 87 ++++ qdf/inc/qdf_nbuf_frag.h | 236 +++++++++- qdf/linux/src/i_qdf_nbuf.h | 32 +- qdf/linux/src/i_qdf_nbuf_frag.h | 79 +++- qdf/linux/src/qdf_mem.c | 2 + qdf/linux/src/qdf_module.c | 2 + qdf/linux/src/qdf_nbuf.c | 140 +++++- qdf/linux/src/qdf_nbuf_frag.c | 758 ++++++++++++++++++++++++++++++++ 8 files changed, 1299 insertions(+), 37 deletions(-) diff --git a/qdf/inc/qdf_nbuf.h b/qdf/inc/qdf_nbuf.h index 3d66a6389e..9573d21478 100644 --- a/qdf/inc/qdf_nbuf.h +++ b/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); } +#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 * 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); } +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 #include #else diff --git a/qdf/inc/qdf_nbuf_frag.h b/qdf/inc/qdf_nbuf_frag.h index b19e390eb8..61b42caa0d 100644 --- a/qdf/inc/qdf_nbuf_frag.h +++ b/qdf/inc/qdf_nbuf_frag.h @@ -24,6 +24,7 @@ #ifndef _QDF_NBUF_FRAG_H #define _QDF_NBUF_FRAG_H +#include #include #include @@ -37,6 +38,222 @@ typedef __qdf_frag_t qdf_frag_t; */ #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 * @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_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 */ diff --git a/qdf/linux/src/i_qdf_nbuf.h b/qdf/linux/src/i_qdf_nbuf.h index 48e9e5fef6..c82c6b3132 100644 --- a/qdf/linux/src/i_qdf_nbuf.h +++ b/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); 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 */ @@ -1045,9 +1056,10 @@ static inline struct sk_buff *__qdf_nbuf_clone(struct sk_buff *skb) struct sk_buff *skb_new = NULL; 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); - + } 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; 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); - + } return skb_new; } @@ -2060,17 +2073,6 @@ __qdf_nbuf_headlen(struct sk_buff *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. * @buf: sk buff diff --git a/qdf/linux/src/i_qdf_nbuf_frag.h b/qdf/linux/src/i_qdf_nbuf_frag.h index 434d71847f..a83aa07374 100644 --- a/qdf/linux/src/i_qdf_nbuf_frag.h +++ b/qdf/linux/src/i_qdf_nbuf_frag.h @@ -27,11 +27,76 @@ #include #include +#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 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 */ @@ -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 * @vaddr: Frag address to be freed + * + * Return: none */ 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) { - 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 */ diff --git a/qdf/linux/src/qdf_mem.c b/qdf/linux/src/qdf_mem.c index 3ebd86d7e0..d2efd29fe7 100644 --- a/qdf/linux/src/qdf_mem.c +++ b/qdf/linux/src/qdf_mem.c @@ -2259,6 +2259,7 @@ void qdf_mem_init(void) { qdf_mem_debug_init(); qdf_net_buf_debug_init(); + qdf_frag_debug_init(); qdf_mem_debugfs_init(); qdf_mem_debug_debugfs_init(); } @@ -2268,6 +2269,7 @@ void qdf_mem_exit(void) { qdf_mem_debug_debugfs_exit(); qdf_mem_debugfs_exit(); + qdf_frag_debug_exit(); qdf_net_buf_debug_exit(); qdf_mem_debug_exit(); } diff --git a/qdf/linux/src/qdf_module.c b/qdf/linux/src/qdf_module.c index 9c1529206c..01eba3d11f 100644 --- a/qdf/linux/src/qdf_module.c +++ b/qdf/linux/src/qdf_module.c @@ -55,6 +55,7 @@ int qdf_mod_init(void) qdf_logging_init(); qdf_perfmod_init(); qdf_nbuf_mod_init(); + qdf_frag_mod_init(); qdf_event_list_init(); return 0; @@ -75,6 +76,7 @@ void qdf_mod_exit(void) #endif { qdf_event_list_destroy(); + qdf_frag_mod_exit(); qdf_nbuf_mod_exit(); qdf_perfmod_exit(); qdf_logging_exit(); diff --git a/qdf/linux/src/qdf_nbuf.c b/qdf/linux/src/qdf_nbuf.c index 90f053f917..a1132cfc22 100644 --- a/qdf/linux/src/qdf_nbuf.c +++ b/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)) return; + qdf_frag_count_dec(qdf_nbuf_get_nr_frags(skb)); qdf_nbuf_count_dec(skb); qdf_mem_skb_dec(skb->truesize); 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) { qdf_nbuf_t ext_list; + qdf_frag_t p_frag; + uint32_t num_nr_frags; + uint32_t idx = 0; if (qdf_unlikely(!nbuf)) 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_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 */ ext_list = qdf_nbuf_get_ext_list(nbuf); 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) { + uint32_t num_nr_frags; + uint32_t idx = 0; + qdf_frag_t p_frag; + qdf_nbuf_t cloned_buf = __qdf_nbuf_clone(buf); 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)) 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 */ qdf_net_buf_debug_add_node(cloned_buf, 0, func, line); 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_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); + } } 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 */ diff --git a/qdf/linux/src/qdf_nbuf_frag.c b/qdf/linux/src/qdf_nbuf_frag.c index 135fadc1ea..0beaa207c1 100644 --- a/qdf/linux/src/qdf_nbuf_frag.c +++ b/qdf/linux/src/qdf_nbuf_frag.c @@ -21,8 +21,766 @@ * QCA driver framework(QDF) network nbuf frag management APIs */ +#include +#include +#include #include #include +#include +#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) QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf,