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
This commit is contained in:

committed by
snandini

parent
ffaafd74ef
commit
d47e448d58
@@ -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 <i_qdf_nbuf_api_w.h>
|
||||
#else
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#ifndef _QDF_NBUF_FRAG_H
|
||||
#define _QDF_NBUF_FRAG_H
|
||||
|
||||
#include <qdf_util.h>
|
||||
#include <i_qdf_trace.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
|
||||
|
||||
#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 */
|
||||
|
@@ -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
|
||||
|
@@ -27,11 +27,76 @@
|
||||
#include <qdf_net_types.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 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 */
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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 */
|
||||
|
@@ -21,8 +21,766 @@
|
||||
* 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_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)
|
||||
QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf,
|
||||
|
Reference in New Issue
Block a user