qcacmn: Extract memory debugging features from qdf_mem

When memory debugging is enabled, qdf_mem_malloc and qdf_mem_free do a
number of memory corruption checks. Extract these checks into their own
functions to allow other memory allocation/free logic to leverage the
same logic.

Change-Id: I07802093119c90b3d8c40a50d5b4bb152cb8243f
CRs-Fixed: 2136659
This commit is contained in:
Dustin Brown
2017-11-01 11:58:42 -07:00
committed by snandini
parent fc5e85f7be
commit 7768da6890
2 changed files with 305 additions and 290 deletions

View File

@@ -96,9 +96,36 @@ void qdf_mem_init(void);
void qdf_mem_exit(void); void qdf_mem_exit(void);
#ifdef MEMORY_DEBUG #ifdef MEMORY_DEBUG
/**
* qdf_mem_malloc_debug() - debug version of QDF memory allocation API
* @size: Number of bytes of memory to allocate.
* @file: File name of the call site
* @line: Line number of the call site
*
* This function will dynamicallly allocate the specified number of bytes of
* memory and add it to the qdf tracking list to check for memory leaks and
* corruptions
*
* Return: A valid memory location on success, or NULL on failure
*/
void *qdf_mem_malloc_debug(size_t size, const char *file, uint32_t line);
#define qdf_mem_malloc(size) \ #define qdf_mem_malloc(size) \
qdf_mem_malloc_debug(size, __FILE__, __LINE__) qdf_mem_malloc_debug(size, __FILE__, __LINE__)
void *qdf_mem_malloc_debug(size_t size, char *file_name, uint32_t line_num);
/**
* qdf_mem_free_debug() - debug version of qdf_mem_free
* @ptr: Pointer to the starting address of the memory to be freed.
*
* This function will free the memory pointed to by 'ptr'. It also checks for
* memory corruption, underrun, overrun, double free, domain mismatch, etc.
*
* Return: none
*/
void qdf_mem_free_debug(void *ptr, const char *file, uint32_t line);
#define qdf_mem_free(ptr) \
qdf_mem_free_debug(ptr, __FILE__, __LINE__)
/** /**
* qdf_mem_check_for_leaks() - Assert that the current memory domain is empty * qdf_mem_check_for_leaks() - Assert that the current memory domain is empty
@@ -133,20 +160,20 @@ void *qdf_mem_malloc_debug(size_t size, char *file_name, uint32_t line_num);
void qdf_mem_check_for_leaks(void); void qdf_mem_check_for_leaks(void);
#else #else
void *qdf_mem_malloc(qdf_size_t size); void *qdf_mem_malloc(qdf_size_t size);
/**
* qdf_mem_free() - free QDF memory
* @ptr: Pointer to the starting address of the memory to be freed.
*
* Return: None
*/
void qdf_mem_free(void *ptr);
static inline void qdf_mem_check_for_leaks(void) { } static inline void qdf_mem_check_for_leaks(void) { }
#endif /* MEMORY_DEBUG */ #endif /* MEMORY_DEBUG */
void *qdf_mem_alloc_outline(qdf_device_t osdev, qdf_size_t size); void *qdf_mem_alloc_outline(qdf_device_t osdev, qdf_size_t size);
/**
* qdf_mem_free() - free QDF memory
* @ptr: Pointer to the starting address of the memory to be free'd.
* This function will free the memory pointed to by 'ptr'.
* Return:
* None
*/
void qdf_mem_free(void *ptr);
void qdf_mem_set(void *ptr, uint32_t num_bytes, uint32_t value); void qdf_mem_set(void *ptr, uint32_t num_bytes, uint32_t value);
void qdf_mem_zero(void *ptr, uint32_t num_bytes); void qdf_mem_zero(void *ptr, uint32_t num_bytes);

View File

@@ -60,6 +60,10 @@
#include "qdf_debug_domain.h" #include "qdf_debug_domain.h"
#include <qdf_list.h> #include <qdf_list.h>
/* Preprocessor Definitions and Constants */
#define QDF_MEM_MAX_MALLOC (1024 * 1024) /* 1MiB */
#define QDF_MEM_WARN_THRESHOLD 300 /* ms */
static qdf_list_t qdf_mem_domains[QDF_DEBUG_DOMAIN_COUNT]; static qdf_list_t qdf_mem_domains[QDF_DEBUG_DOMAIN_COUNT];
static qdf_spinlock_t qdf_mem_list_lock; static qdf_spinlock_t qdf_mem_list_lock;
@@ -69,29 +73,175 @@ static inline qdf_list_t *qdf_mem_list_get(enum qdf_debug_domain domain)
} }
/** /**
* struct s_qdf_mem_struct - memory object to dubug * struct qdf_mem_header - memory object to dubug
* @node: node to the list * @node: node to the list
* @filename: name of file * @domain: the active memory domain at time of allocation
* @line_num: line number * @freed: flag set during free, used to detect double frees
* @size: size of the file * Use uint8_t so we can detect corruption
* @header: array that contains header * @file: name of the file the allocation was made from
* @in_use: memory usage count * @line: line number of the file the allocation was made from
* @size: size of the allocation in bytes
* @header: a known value, used to detect out-of-bounds access
*/ */
struct s_qdf_mem_struct { struct qdf_mem_header {
qdf_list_node_t node; qdf_list_node_t node;
char *file_name; enum qdf_debug_domain domain;
uint32_t line_num; uint8_t freed;
const char *file;
uint32_t line;
uint32_t size; uint32_t size;
uint64_t header; uint64_t header;
enum qdf_debug_domain domain;
}; };
static uint64_t WLAN_MEM_HEADER = 0x6162636465666768; static uint64_t WLAN_MEM_HEADER = 0x6162636465666768;
static uint64_t WLAN_MEM_TAIL = 0x8081828384858687; static uint64_t WLAN_MEM_TRAILER = 0x8081828384858687;
#endif /* MEMORY_DEBUG */
/* Preprocessor Definitions and Constants */ static inline struct qdf_mem_header *qdf_mem_get_header(void *ptr)
#define QDF_GET_MEMORY_TIME_THRESHOLD 300 {
return (struct qdf_mem_header *)ptr - 1;
}
static inline uint64_t *qdf_mem_get_trailer(struct qdf_mem_header *header)
{
return (uint64_t *)((void *)(header + 1) + header->size);
}
static inline void *qdf_mem_get_ptr(struct qdf_mem_header *header)
{
return (void *)(header + 1);
}
/* number of bytes needed for the qdf memory debug information */
#define QDF_MEM_DEBUG_SIZE \
(sizeof(struct qdf_mem_header) + sizeof(WLAN_MEM_TRAILER))
static void qdf_mem_header_init(struct qdf_mem_header *header, qdf_size_t size,
const char *file, uint32_t line)
{
QDF_BUG(header);
if (!header)
return;
header->domain = qdf_debug_domain_get();
header->freed = false;
header->file = file;
header->line = line;
header->size = size;
header->header = WLAN_MEM_HEADER;
*qdf_mem_get_trailer(header) = WLAN_MEM_TRAILER;
}
enum qdf_mem_validation_bitmap {
QDF_MEM_BAD_HEADER = 1 << 0,
QDF_MEM_BAD_TRAILER = 1 << 1,
QDF_MEM_BAD_SIZE = 1 << 2,
QDF_MEM_DOUBLE_FREE = 1 << 3,
QDF_MEM_BAD_FREED = 1 << 4,
QDF_MEM_BAD_NODE = 1 << 5,
QDF_MEM_BAD_DOMAIN = 1 << 6,
QDF_MEM_WRONG_DOMAIN = 1 << 7,
};
/**
* qdf_mem_validate_list_node() - validate that the node is in a list
* @qdf_node: node to check for being in a list
*
* Return: true if the node validly linked in an anchored doubly linked list
*/
static bool qdf_mem_validate_list_node(qdf_list_node_t *qdf_node)
{
struct list_head *node = qdf_node;
/*
* if the node is an empty list, it is not tied to an anchor node
* and must have been removed with list_del_init
*/
if (list_empty(node))
return false;
if (!node->prev || !node->next)
return false;
if (node->prev->next != node || node->next->prev != node)
return false;
return true;
}
static enum qdf_mem_validation_bitmap
qdf_mem_header_validate(struct qdf_mem_header *header,
enum qdf_debug_domain domain)
{
enum qdf_mem_validation_bitmap error_bitmap = 0;
if (header->header != WLAN_MEM_HEADER)
error_bitmap |= QDF_MEM_BAD_HEADER;
if (header->size > QDF_MEM_MAX_MALLOC)
error_bitmap |= QDF_MEM_BAD_SIZE;
else if (*qdf_mem_get_trailer(header) != WLAN_MEM_TRAILER)
error_bitmap |= QDF_MEM_BAD_TRAILER;
if (header->freed == true)
error_bitmap |= QDF_MEM_DOUBLE_FREE;
else if (header->freed)
error_bitmap |= QDF_MEM_BAD_FREED;
if (!qdf_mem_validate_list_node(&header->node))
error_bitmap |= QDF_MEM_BAD_NODE;
if (header->domain < QDF_DEBUG_DOMAIN_INIT ||
header->domain >= QDF_DEBUG_DOMAIN_COUNT)
error_bitmap |= QDF_MEM_BAD_DOMAIN;
else if (header->domain != domain)
error_bitmap |= QDF_MEM_WRONG_DOMAIN;
return error_bitmap;
}
static void
qdf_mem_header_assert_valid(struct qdf_mem_header *header,
enum qdf_debug_domain current_domain,
enum qdf_mem_validation_bitmap error_bitmap,
const char *file,
uint32_t line)
{
if (!error_bitmap)
return;
if (error_bitmap & QDF_MEM_BAD_HEADER)
qdf_err("Corrupted memory header 0x%llx (expected 0x%llx)",
header->header, WLAN_MEM_HEADER);
if (error_bitmap & QDF_MEM_BAD_SIZE)
qdf_err("Corrupted memory size %u (expected < %d)",
header->size, QDF_MEM_MAX_MALLOC);
if (error_bitmap & QDF_MEM_BAD_TRAILER)
qdf_err("Corrupted memory trailer 0x%llx (expected 0x%llx)",
*qdf_mem_get_trailer(header), WLAN_MEM_TRAILER);
if (error_bitmap & QDF_MEM_DOUBLE_FREE)
qdf_err("Memory has previously been freed");
if (error_bitmap & QDF_MEM_BAD_FREED)
qdf_err("Corrupted memory freed flag 0x%x", header->freed);
if (error_bitmap & QDF_MEM_BAD_NODE)
qdf_err("Corrupted memory header node or double free");
if (error_bitmap & QDF_MEM_BAD_DOMAIN)
qdf_err("Corrupted memory domain 0x%x", header->domain);
if (error_bitmap & QDF_MEM_WRONG_DOMAIN)
qdf_err("Memory domain mismatch; found %s(%d), expected %s(%d)",
qdf_debug_domain_name(header->domain), header->domain,
qdf_debug_domain_name(current_domain), current_domain);
panic("A fatal memory error was detected @ %s:%d",
kbasename(file), line);
}
#endif /* MEMORY_DEBUG */
u_int8_t prealloc_disabled = 1; u_int8_t prealloc_disabled = 1;
qdf_declare_param(prealloc_disabled, byte); qdf_declare_param(prealloc_disabled, byte);
@@ -171,15 +321,15 @@ static int seq_printf_printer(void *priv, const char *fmt, ...)
/** /**
* struct __qdf_mem_info - memory statistics * struct __qdf_mem_info - memory statistics
* @file_name: the file which allocated memory * @file: the file which allocated memory
* @line_num: the line at which allocation happened * @line: the line at which allocation happened
* @size: the size of allocation * @size: the size of allocation
* @count: how many allocations of same type * @count: how many allocations of same type
* *
*/ */
struct __qdf_mem_info { struct __qdf_mem_info {
char *file_name; const char *file;
uint32_t line_num; uint32_t line;
uint32_t size; uint32_t size;
uint32_t count; uint32_t count;
}; };
@@ -230,8 +380,8 @@ static void qdf_mem_meta_table_print(struct __qdf_mem_info *table,
table[i].count, table[i].count,
table[i].size, table[i].size,
table[i].count * table[i].size, table[i].count * table[i].size,
kbasename(table[i].file_name), kbasename(table[i].file),
table[i].line_num); table[i].line);
} }
} }
@@ -243,21 +393,21 @@ static void qdf_mem_meta_table_print(struct __qdf_mem_info *table,
* Return: true if the table is full after inserting, false otherwise * Return: true if the table is full after inserting, false otherwise
*/ */
static bool qdf_mem_meta_table_insert(struct __qdf_mem_info *table, static bool qdf_mem_meta_table_insert(struct __qdf_mem_info *table,
struct s_qdf_mem_struct *meta) struct qdf_mem_header *meta)
{ {
int i; int i;
for (i = 0; i < QDF_MEM_STAT_TABLE_SIZE; i++) { for (i = 0; i < QDF_MEM_STAT_TABLE_SIZE; i++) {
if (!table[i].count) { if (!table[i].count) {
table[i].file_name = meta->file_name; table[i].file = meta->file;
table[i].line_num = meta->line_num; table[i].line = meta->line;
table[i].size = meta->size; table[i].size = meta->size;
table[i].count = 1; table[i].count = 1;
break; break;
} }
if (table[i].file_name == meta->file_name && if (table[i].file == meta->file &&
table[i].line_num == meta->line_num && table[i].line == meta->line &&
table[i].size == meta->size) { table[i].size == meta->size) {
table[i].count++; table[i].count++;
break; break;
@@ -291,7 +441,7 @@ static void qdf_mem_domain_print(qdf_list_t *domain,
qdf_spin_lock(&qdf_mem_list_lock); qdf_spin_lock(&qdf_mem_list_lock);
status = qdf_list_peek_front(domain, &node); status = qdf_list_peek_front(domain, &node);
while (QDF_IS_STATUS_SUCCESS(status)) { while (QDF_IS_STATUS_SUCCESS(status)) {
struct s_qdf_mem_struct *meta = (struct s_qdf_mem_struct *)node; struct qdf_mem_header *meta = (struct qdf_mem_header *)node;
bool is_full = qdf_mem_meta_table_insert(table, meta); bool is_full = qdf_mem_meta_table_insert(table, meta);
qdf_spin_unlock(&qdf_mem_list_lock); qdf_spin_unlock(&qdf_mem_list_lock);
@@ -735,16 +885,18 @@ EXPORT_SYMBOL(qdf_mem_zero_outline);
*/ */
static void *qdf_mem_prealloc_get(size_t size) static void *qdf_mem_prealloc_get(size_t size)
{ {
void *mem; void *ptr;
if (size <= WCNSS_PRE_ALLOC_GET_THRESHOLD) if (size <= WCNSS_PRE_ALLOC_GET_THRESHOLD)
return NULL; return NULL;
mem = wcnss_prealloc_get(size); ptr = wcnss_prealloc_get(size);
if (mem) if (!ptr)
memset(mem, 0, size); return NULL;
return mem; memset(ptr, 0, size);
return ptr;
} }
static inline bool qdf_mem_prealloc_put(void *ptr) static inline bool qdf_mem_prealloc_put(void *ptr)
@@ -763,6 +915,14 @@ static inline bool qdf_mem_prealloc_put(void *ptr)
} }
#endif /* CONFIG_WCNSS_MEM_PRE_ALLOC */ #endif /* CONFIG_WCNSS_MEM_PRE_ALLOC */
static int qdf_mem_malloc_flags(void)
{
if (in_interrupt() || irqs_disabled() || in_atomic())
return GFP_ATOMIC;
return GFP_KERNEL;
}
/* External Function implementation */ /* External Function implementation */
#ifdef MEMORY_DEBUG #ifdef MEMORY_DEBUG
@@ -867,245 +1027,83 @@ static void qdf_mem_debug_exit(void)
qdf_spinlock_destroy(&qdf_mem_list_lock); qdf_spinlock_destroy(&qdf_mem_list_lock);
} }
/** void *qdf_mem_malloc_debug(size_t size, const char *file, uint32_t line)
* qdf_mem_malloc_debug() - debug version of QDF memory allocation API
* @size: Number of bytes of memory to allocate.
* @file_name: File name from which memory allocation is called
* @line_num: Line number from which memory allocation is called
*
* This function will dynamicallly allocate the specified number of bytes of
* memory and ad it in qdf tracking list to check against memory leaks and
* corruptions
*
* Return:
* Upon successful allocate, returns a non-NULL pointer to the allocated
* memory. If this function is unable to allocate the amount of memory
* specified (for any reason) it returns %NULL.
*/
void *qdf_mem_malloc_debug(size_t size, char *file_name, uint32_t line_num)
{ {
QDF_STATUS status;
enum qdf_debug_domain current_domain = qdf_debug_domain_get(); enum qdf_debug_domain current_domain = qdf_debug_domain_get();
qdf_list_t *mem_list = qdf_mem_list_get(current_domain); qdf_list_t *mem_list = qdf_mem_list_get(current_domain);
struct s_qdf_mem_struct *mem_struct; struct qdf_mem_header *header;
void *mem_ptr = NULL; void *ptr;
uint32_t new_size; unsigned long start, duration;
int flags = GFP_KERNEL;
unsigned long time_before_kzalloc;
if (size > (1024 * 1024) || size == 0) { if (!size || size > QDF_MEM_MAX_MALLOC) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, qdf_err("Cannot malloc %zu bytes @ %s:%d", size, file, line);
"%s: called with invalid arg; passed in %zu !!!",
__func__, size);
return NULL; return NULL;
} }
mem_ptr = qdf_mem_prealloc_get(size); ptr = qdf_mem_prealloc_get(size);
if (mem_ptr) if (ptr)
return mem_ptr; return ptr;
if (in_interrupt() || irqs_disabled() || in_atomic()) start = qdf_mc_timer_get_system_time();
flags = GFP_ATOMIC; header = kzalloc(size + QDF_MEM_DEBUG_SIZE, qdf_mem_malloc_flags());
duration = qdf_mc_timer_get_system_time() - start;
new_size = sizeof(*mem_struct) + size + sizeof(WLAN_MEM_TAIL); if (duration > QDF_MEM_WARN_THRESHOLD)
time_before_kzalloc = qdf_mc_timer_get_system_time(); qdf_warn("Malloc slept; %lums, %zuB @ %s:%d",
mem_struct = kzalloc(new_size, flags); duration, size, file, line);
/**
* If time taken by kmalloc is greater than
* QDF_GET_MEMORY_TIME_THRESHOLD msec
*/
if (qdf_mc_timer_get_system_time() - time_before_kzalloc >=
QDF_GET_MEMORY_TIME_THRESHOLD)
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
"%s: kzalloc took %lu msec for size %zu called from %p_s at line %d",
__func__,
qdf_mc_timer_get_system_time() - time_before_kzalloc,
size, (void *)_RET_IP_, line_num);
if (mem_struct) { if (!header)
QDF_STATUS status; return NULL;
uint64_t *trailer;
mem_ptr = (void *)(mem_struct + 1); qdf_mem_header_init(header, size, file, line);
trailer = (uint64_t *)(mem_ptr + size); ptr = qdf_mem_get_ptr(header);
mem_struct->file_name = file_name; qdf_spin_lock_irqsave(&qdf_mem_list_lock);
mem_struct->line_num = line_num; status = qdf_list_insert_front(mem_list, &header->node);
mem_struct->size = size; qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
mem_struct->domain = current_domain; if (QDF_IS_STATUS_ERROR(status))
mem_struct->header = WLAN_MEM_HEADER; qdf_err("Failed to insert memory header; status %d", status);
*trailer = WLAN_MEM_TAIL;
qdf_mem_kmalloc_inc(ksize(mem_struct)); qdf_mem_kmalloc_inc(size);
qdf_spin_lock_irqsave(&qdf_mem_list_lock); return ptr;
status = qdf_list_insert_front(mem_list, &mem_struct->node);
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
if (QDF_IS_STATUS_ERROR(status))
qdf_err("Unable to insert into list status %d", status);
}
return mem_ptr;
} }
EXPORT_SYMBOL(qdf_mem_malloc_debug); qdf_export_symbol(qdf_mem_malloc_debug);
/** void qdf_mem_free_debug(void *ptr, const char *file, uint32_t line)
* qdf_mem_validate_node_for_free() - validate that the node is in a list
* @qdf_node: node to check for being in a list
*
* qdf_node should be a non null value.
*
* Return: true if the node validly linked in an anchored doubly linked list
*/
static bool qdf_mem_validate_node_for_free(qdf_list_node_t *qdf_node)
{
struct list_head *node = qdf_node;
/*
* if the node is an empty list, it is not tied to an anchor node
* and must have been removed with list_del_init
*/
if (list_empty(node))
return false;
if (node->prev == NULL)
return false;
if (node->next == NULL)
return false;
if (node->prev->next != node)
return false;
if (node->next->prev != node)
return false;
return true;
}
/**
* qdf_mem_free() - QDF memory free API
* @ptr: Pointer to the starting address of the memory to be free'd.
*
* This function will free the memory pointed to by 'ptr'. It also checks
* is memory is corrupted or getting double freed and panic.
*
* Return: none
*/
void qdf_mem_free(void *ptr)
{ {
enum qdf_debug_domain current_domain = qdf_debug_domain_get(); enum qdf_debug_domain current_domain = qdf_debug_domain_get();
qdf_list_t *mem_list = qdf_mem_list_get(current_domain); struct qdf_mem_header *header;
struct s_qdf_mem_struct *mem_struct; enum qdf_mem_validation_bitmap error_bitmap;
uint64_t *trailer;
/* freeing a null pointer is valid */ /* freeing a null pointer is valid */
if (qdf_unlikely(ptr == NULL)) if (qdf_unlikely(!ptr))
return; return;
if (qdf_mem_prealloc_put(ptr)) if (qdf_mem_prealloc_put(ptr))
return; return;
mem_struct = ((struct s_qdf_mem_struct *)ptr) - 1; if (qdf_unlikely((qdf_size_t)ptr <= sizeof(*header)))
panic("Failed to free invalid memory location %pK", ptr);
if (qdf_unlikely(mem_struct == NULL)) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
"%s: null mem_struct", __func__);
QDF_BUG(0);
}
trailer = (uint64_t *)(ptr + mem_struct->size);
qdf_spin_lock_irqsave(&qdf_mem_list_lock); qdf_spin_lock_irqsave(&qdf_mem_list_lock);
header = qdf_mem_get_header(ptr);
/* error_bitmap = qdf_mem_header_validate(header, current_domain);
* invalid memory access when checking the header/tailer if (!error_bitmap) {
* would be a use after free and would indicate a double free header->freed = true;
* or invalid pointer passed. list_del_init(&header->node);
*/ qdf_mem_list_get(header->domain)->count--;
if (mem_struct->header != WLAN_MEM_HEADER)
goto error;
/*
* invalid memory access while checking validate node
* would indicate corruption in the nodes pointed to
*/
if (!qdf_mem_validate_node_for_free(&mem_struct->node))
goto error;
/*
* invalid memory access here is unlikely and would imply
* that the size value was corrupted/incorrect.
* It is unlikely that the above checks would pass in a
* double free case.
*/
if (*trailer != WLAN_MEM_TAIL)
goto error;
/* make sure the memory belongs to the current domain */
if (mem_struct->domain != current_domain) {
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
qdf_err("Memory domain mismatch; found %s, expected %s (%s:%u)",
qdf_debug_domain_name(mem_struct->domain),
qdf_debug_domain_name(current_domain),
kbasename(mem_struct->file_name),
mem_struct->line_num);
qdf_mem_leak_panic();
/* continue de-allocation if we didn't crash */
qdf_spin_lock_irqsave(&qdf_mem_list_lock);
} }
/*
* make the node an empty list before doing the spin unlock
* The empty list check will guarantee that we avoid a race condition.
*/
list_del_init(&mem_struct->node);
mem_list->count--;
qdf_mem_kmalloc_dec(ksize(mem_struct));
kfree(mem_struct);
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock); qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
return; qdf_mem_header_assert_valid(header, current_domain, error_bitmap,
file, line);
error: qdf_mem_kmalloc_dec(header->size);
if (!qdf_list_has_node(mem_list, &mem_struct->node)) { kfree(header);
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
"%s: Unallocated memory (double free?)",
__func__);
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
QDF_BUG(0);
}
if (mem_struct->header != WLAN_MEM_HEADER) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
"Memory Header is corrupted.");
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
QDF_BUG(0);
}
if (!qdf_mem_validate_node_for_free(&mem_struct->node)) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
"Memory_struct is corrupted.");
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
QDF_BUG(0);
}
if (*trailer != WLAN_MEM_TAIL) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
"Memory Trailer is corrupted. mem_info: Filename %s, line_num %d",
mem_struct->file_name, (int)mem_struct->line_num);
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
QDF_BUG(0);
}
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
"%s unexpected error", __func__);
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
QDF_BUG(0);
} }
EXPORT_SYMBOL(qdf_mem_free); qdf_export_symbol(qdf_mem_free_debug);
void qdf_mem_check_for_leaks(void) void qdf_mem_check_for_leaks(void)
{ {
@@ -1139,23 +1137,19 @@ static void qdf_mem_debug_exit(void) {}
*/ */
void *qdf_mem_malloc(size_t size) void *qdf_mem_malloc(size_t size)
{ {
int flags = GFP_KERNEL; void *ptr;
void *mem;
mem = qdf_mem_prealloc_get(size); ptr = qdf_mem_prealloc_get(size);
if (mem) if (ptr)
return mem; return ptr;
if (in_interrupt() || irqs_disabled() || in_atomic()) ptr = kzalloc(size, qdf_mem_malloc_flags());
flags = GFP_ATOMIC; if (!ptr)
return NULL;
mem = kzalloc(size, flags); qdf_mem_kmalloc_inc(ksize(ptr));
if (mem)
qdf_mem_kmalloc_inc(ksize(mem));
return mem;
return ptr;
} }
EXPORT_SYMBOL(qdf_mem_malloc); EXPORT_SYMBOL(qdf_mem_malloc);
@@ -1530,50 +1524,44 @@ void *qdf_mem_alloc_consistent(qdf_device_t osdev, void *dev, qdf_size_t size,
void *qdf_mem_alloc_consistent(qdf_device_t osdev, void *dev, qdf_size_t size, void *qdf_mem_alloc_consistent(qdf_device_t osdev, void *dev, qdf_size_t size,
qdf_dma_addr_t *phy_addr) qdf_dma_addr_t *phy_addr)
{ {
int flags = GFP_KERNEL; void *vaddr = NULL;
void *alloc_mem = NULL; int i;
int alloc_try_high_mem = 0;
if (in_interrupt() || irqs_disabled() || in_atomic())
flags = GFP_ATOMIC;
*phy_addr = 0; *phy_addr = 0;
while (alloc_try_high_mem++ < QDF_MEM_ALLOC_X86_MAX_RETRIES) { for (i = 0; i < QDF_MEM_ALLOC_X86_MAX_RETRIES; i++) {
alloc_mem = dma_alloc_coherent(dev, size, phy_addr, flags); vaddr = dma_alloc_coherent(dev, size, phy_addr,
qdf_mem_malloc_flags());
if (alloc_mem == NULL) { if (!vaddr) {
qdf_print("%s failed , size: %zu!\n", __func__, size); qdf_print("%s failed , size: %zu!\n", __func__, size);
return NULL; return NULL;
} }
if (*phy_addr < QCA8074_RAM_BASE) { if (*phy_addr >= QCA8074_RAM_BASE)
dma_free_coherent(dev, size, alloc_mem, *phy_addr); return vaddr;
alloc_mem = NULL;
} else dma_free_coherent(dev, size, vaddr, *phy_addr);
break;
} }
return alloc_mem; return NULL;
} }
#else #else
void *qdf_mem_alloc_consistent(qdf_device_t osdev, void *dev, qdf_size_t size, void *qdf_mem_alloc_consistent(qdf_device_t osdev, void *dev, qdf_size_t size,
qdf_dma_addr_t *phy_addr) qdf_dma_addr_t *phy_addr)
{ {
int flags = GFP_KERNEL; void *ptr;
void *alloc_mem = NULL;
if (in_interrupt() || irqs_disabled() || in_atomic()) ptr = dma_alloc_coherent(dev, size, phy_addr, qdf_mem_malloc_flags());
flags = GFP_ATOMIC; if (!ptr) {
qdf_warn("Warning: unable to alloc consistent memory of size %zu!\n",
size);
return NULL;
}
alloc_mem = dma_alloc_coherent(dev, size, phy_addr, flags); qdf_mem_dma_inc(size);
if (alloc_mem == NULL)
qdf_print("%s Warning: unable to alloc consistent memory of size %zu!\n",
__func__, size);
else
qdf_mem_dma_inc(size);
return alloc_mem; return ptr;
} }
#endif #endif