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:
Harsh Kumar Bijlani
2020-07-13 20:02:31 +05:30
committed by snandini
父節點 ffaafd74ef
當前提交 d47e448d58
共有 8 個文件被更改,包括 1299 次插入37 次删除

查看文件

@@ -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)
{
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,