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);
#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) \
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
@@ -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);
#else
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) { }
#endif /* MEMORY_DEBUG */
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_zero(void *ptr, uint32_t num_bytes);

View File

@@ -60,6 +60,10 @@
#include "qdf_debug_domain.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_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
* @filename: name of file
* @line_num: line number
* @size: size of the file
* @header: array that contains header
* @in_use: memory usage count
* @domain: the active memory domain at time of allocation
* @freed: flag set during free, used to detect double frees
* Use uint8_t so we can detect corruption
* @file: name of the file the allocation was made from
* @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;
char *file_name;
uint32_t line_num;
enum qdf_debug_domain domain;
uint8_t freed;
const char *file;
uint32_t line;
uint32_t size;
uint64_t header;
enum qdf_debug_domain domain;
};
static uint64_t WLAN_MEM_HEADER = 0x6162636465666768;
static uint64_t WLAN_MEM_TAIL = 0x8081828384858687;
#endif /* MEMORY_DEBUG */
static uint64_t WLAN_MEM_TRAILER = 0x8081828384858687;
/* Preprocessor Definitions and Constants */
#define QDF_GET_MEMORY_TIME_THRESHOLD 300
static inline struct qdf_mem_header *qdf_mem_get_header(void *ptr)
{
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;
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
* @file_name: the file which allocated memory
* @line_num: the line at which allocation happened
* @file: the file which allocated memory
* @line: the line at which allocation happened
* @size: the size of allocation
* @count: how many allocations of same type
*
*/
struct __qdf_mem_info {
char *file_name;
uint32_t line_num;
const char *file;
uint32_t line;
uint32_t size;
uint32_t count;
};
@@ -230,8 +380,8 @@ static void qdf_mem_meta_table_print(struct __qdf_mem_info *table,
table[i].count,
table[i].size,
table[i].count * table[i].size,
kbasename(table[i].file_name),
table[i].line_num);
kbasename(table[i].file),
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
*/
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;
for (i = 0; i < QDF_MEM_STAT_TABLE_SIZE; i++) {
if (!table[i].count) {
table[i].file_name = meta->file_name;
table[i].line_num = meta->line_num;
table[i].file = meta->file;
table[i].line = meta->line;
table[i].size = meta->size;
table[i].count = 1;
break;
}
if (table[i].file_name == meta->file_name &&
table[i].line_num == meta->line_num &&
if (table[i].file == meta->file &&
table[i].line == meta->line &&
table[i].size == meta->size) {
table[i].count++;
break;
@@ -291,7 +441,7 @@ static void qdf_mem_domain_print(qdf_list_t *domain,
qdf_spin_lock(&qdf_mem_list_lock);
status = qdf_list_peek_front(domain, &node);
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);
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)
{
void *mem;
void *ptr;
if (size <= WCNSS_PRE_ALLOC_GET_THRESHOLD)
return NULL;
mem = wcnss_prealloc_get(size);
if (mem)
memset(mem, 0, size);
ptr = wcnss_prealloc_get(size);
if (!ptr)
return NULL;
return mem;
memset(ptr, 0, size);
return 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 */
static int qdf_mem_malloc_flags(void)
{
if (in_interrupt() || irqs_disabled() || in_atomic())
return GFP_ATOMIC;
return GFP_KERNEL;
}
/* External Function implementation */
#ifdef MEMORY_DEBUG
@@ -867,245 +1027,83 @@ static void qdf_mem_debug_exit(void)
qdf_spinlock_destroy(&qdf_mem_list_lock);
}
/**
* 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)
void *qdf_mem_malloc_debug(size_t size, const char *file, uint32_t line)
{
QDF_STATUS status;
enum qdf_debug_domain current_domain = qdf_debug_domain_get();
qdf_list_t *mem_list = qdf_mem_list_get(current_domain);
struct s_qdf_mem_struct *mem_struct;
void *mem_ptr = NULL;
uint32_t new_size;
int flags = GFP_KERNEL;
unsigned long time_before_kzalloc;
struct qdf_mem_header *header;
void *ptr;
unsigned long start, duration;
if (size > (1024 * 1024) || size == 0) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
"%s: called with invalid arg; passed in %zu !!!",
__func__, size);
if (!size || size > QDF_MEM_MAX_MALLOC) {
qdf_err("Cannot malloc %zu bytes @ %s:%d", size, file, line);
return NULL;
}
mem_ptr = qdf_mem_prealloc_get(size);
if (mem_ptr)
return mem_ptr;
ptr = qdf_mem_prealloc_get(size);
if (ptr)
return ptr;
if (in_interrupt() || irqs_disabled() || in_atomic())
flags = GFP_ATOMIC;
start = qdf_mc_timer_get_system_time();
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);
time_before_kzalloc = qdf_mc_timer_get_system_time();
mem_struct = kzalloc(new_size, flags);
/**
* 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 (duration > QDF_MEM_WARN_THRESHOLD)
qdf_warn("Malloc slept; %lums, %zuB @ %s:%d",
duration, size, file, line);
if (mem_struct) {
QDF_STATUS status;
uint64_t *trailer;
if (!header)
return NULL;
mem_ptr = (void *)(mem_struct + 1);
trailer = (uint64_t *)(mem_ptr + size);
mem_struct->file_name = file_name;
mem_struct->line_num = line_num;
mem_struct->size = size;
mem_struct->domain = current_domain;
mem_struct->header = WLAN_MEM_HEADER;
*trailer = WLAN_MEM_TAIL;
qdf_mem_kmalloc_inc(ksize(mem_struct));
qdf_mem_header_init(header, size, file, line);
ptr = qdf_mem_get_ptr(header);
qdf_spin_lock_irqsave(&qdf_mem_list_lock);
status = qdf_list_insert_front(mem_list, &mem_struct->node);
status = qdf_list_insert_front(mem_list, &header->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);
qdf_err("Failed to insert memory header; status %d", status);
qdf_mem_kmalloc_inc(size);
return ptr;
}
qdf_export_symbol(qdf_mem_malloc_debug);
return mem_ptr;
}
EXPORT_SYMBOL(qdf_mem_malloc_debug);
/**
* 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)
void qdf_mem_free_debug(void *ptr, const char *file, uint32_t line)
{
enum qdf_debug_domain current_domain = qdf_debug_domain_get();
qdf_list_t *mem_list = qdf_mem_list_get(current_domain);
struct s_qdf_mem_struct *mem_struct;
uint64_t *trailer;
struct qdf_mem_header *header;
enum qdf_mem_validation_bitmap error_bitmap;
/* freeing a null pointer is valid */
if (qdf_unlikely(ptr == NULL))
if (qdf_unlikely(!ptr))
return;
if (qdf_mem_prealloc_put(ptr))
return;
mem_struct = ((struct s_qdf_mem_struct *)ptr) - 1;
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);
if (qdf_unlikely((qdf_size_t)ptr <= sizeof(*header)))
panic("Failed to free invalid memory location %pK", ptr);
qdf_spin_lock_irqsave(&qdf_mem_list_lock);
/*
* invalid memory access when checking the header/tailer
* would be a use after free and would indicate a double free
* or invalid pointer passed.
*/
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);
header = qdf_mem_get_header(ptr);
error_bitmap = qdf_mem_header_validate(header, current_domain);
if (!error_bitmap) {
header->freed = true;
list_del_init(&header->node);
qdf_mem_list_get(header->domain)->count--;
}
/*
* 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);
return;
qdf_mem_header_assert_valid(header, current_domain, error_bitmap,
file, line);
error:
if (!qdf_list_has_node(mem_list, &mem_struct->node)) {
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);
qdf_mem_kmalloc_dec(header->size);
kfree(header);
}
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)
{
@@ -1139,23 +1137,19 @@ static void qdf_mem_debug_exit(void) {}
*/
void *qdf_mem_malloc(size_t size)
{
int flags = GFP_KERNEL;
void *mem;
void *ptr;
mem = qdf_mem_prealloc_get(size);
if (mem)
return mem;
ptr = qdf_mem_prealloc_get(size);
if (ptr)
return ptr;
if (in_interrupt() || irqs_disabled() || in_atomic())
flags = GFP_ATOMIC;
ptr = kzalloc(size, qdf_mem_malloc_flags());
if (!ptr)
return NULL;
mem = kzalloc(size, flags);
if (mem)
qdf_mem_kmalloc_inc(ksize(mem));
return mem;
qdf_mem_kmalloc_inc(ksize(ptr));
return ptr;
}
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,
qdf_dma_addr_t *phy_addr)
{
int flags = GFP_KERNEL;
void *alloc_mem = NULL;
int alloc_try_high_mem = 0;
if (in_interrupt() || irqs_disabled() || in_atomic())
flags = GFP_ATOMIC;
void *vaddr = NULL;
int i;
*phy_addr = 0;
while (alloc_try_high_mem++ < QDF_MEM_ALLOC_X86_MAX_RETRIES) {
alloc_mem = dma_alloc_coherent(dev, size, phy_addr, flags);
for (i = 0; i < QDF_MEM_ALLOC_X86_MAX_RETRIES; i++) {
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);
return NULL;
}
if (*phy_addr < QCA8074_RAM_BASE) {
dma_free_coherent(dev, size, alloc_mem, *phy_addr);
alloc_mem = NULL;
} else
break;
if (*phy_addr >= QCA8074_RAM_BASE)
return vaddr;
dma_free_coherent(dev, size, vaddr, *phy_addr);
}
return alloc_mem;
return NULL;
}
#else
void *qdf_mem_alloc_consistent(qdf_device_t osdev, void *dev, qdf_size_t size,
qdf_dma_addr_t *phy_addr)
{
int flags = GFP_KERNEL;
void *alloc_mem = NULL;
void *ptr;
if (in_interrupt() || irqs_disabled() || in_atomic())
flags = GFP_ATOMIC;
ptr = dma_alloc_coherent(dev, size, phy_addr, qdf_mem_malloc_flags());
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);
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