|
@@ -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
|
|
|
- * @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
|
|
|
+ * struct qdf_mem_header - memory object to dubug
|
|
|
+ * @node: node to the list
|
|
|
+ * @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
|
|
|
- * @size: the size of allocation
|
|
|
- * @count: how many allocations of same type
|
|
|
+ * @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;
|
|
|
-
|
|
|
- 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 (mem_struct) {
|
|
|
- QDF_STATUS status;
|
|
|
- uint64_t *trailer;
|
|
|
-
|
|
|
- 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_spin_lock_irqsave(&qdf_mem_list_lock);
|
|
|
- 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);
|
|
|
- }
|
|
|
+ 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;
|
|
|
|
|
|
- return mem_ptr;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(qdf_mem_malloc_debug);
|
|
|
+ if (duration > QDF_MEM_WARN_THRESHOLD)
|
|
|
+ qdf_warn("Malloc slept; %lums, %zuB @ %s:%d",
|
|
|
+ duration, size, file, 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 (!header)
|
|
|
+ return NULL;
|
|
|
|
|
|
- if (node->next == NULL)
|
|
|
- return false;
|
|
|
+ qdf_mem_header_init(header, size, file, line);
|
|
|
+ ptr = qdf_mem_get_ptr(header);
|
|
|
|
|
|
- if (node->prev->next != node)
|
|
|
- return false;
|
|
|
+ qdf_spin_lock_irqsave(&qdf_mem_list_lock);
|
|
|
+ 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("Failed to insert memory header; status %d", status);
|
|
|
|
|
|
- if (node->next->prev != node)
|
|
|
- return false;
|
|
|
+ qdf_mem_kmalloc_inc(size);
|
|
|
|
|
|
- return true;
|
|
|
+ return ptr;
|
|
|
}
|
|
|
+qdf_export_symbol(qdf_mem_malloc_debug);
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * 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;
|
|
|
-
|
|
|
-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);
|
|
|
- }
|
|
|
-
|
|
|
- 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);
|
|
|
- }
|
|
|
+ qdf_mem_header_assert_valid(header, current_domain, error_bitmap,
|
|
|
+ file, line);
|
|
|
|
|
|
- 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);
|
|
|
+ qdf_mem_kmalloc_dec(header->size);
|
|
|
+ kfree(header);
|
|
|
}
|
|
|
-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;
|
|
|
-
|
|
|
- mem = qdf_mem_prealloc_get(size);
|
|
|
- if (mem)
|
|
|
- return mem;
|
|
|
-
|
|
|
- if (in_interrupt() || irqs_disabled() || in_atomic())
|
|
|
- flags = GFP_ATOMIC;
|
|
|
+ void *ptr;
|
|
|
|
|
|
- mem = kzalloc(size, flags);
|
|
|
+ ptr = qdf_mem_prealloc_get(size);
|
|
|
+ if (ptr)
|
|
|
+ return ptr;
|
|
|
|
|
|
- if (mem)
|
|
|
- qdf_mem_kmalloc_inc(ksize(mem));
|
|
|
+ ptr = kzalloc(size, qdf_mem_malloc_flags());
|
|
|
+ if (!ptr)
|
|
|
+ return NULL;
|
|
|
|
|
|
- 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);
|
|
|
+ qdf_mem_dma_inc(size);
|
|
|
|
|
|
- return alloc_mem;
|
|
|
+ return ptr;
|
|
|
}
|
|
|
|
|
|
#endif
|