summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCNSS_WLAN Service <cnssbldsw@qualcomm.com>2017-10-03 12:46:18 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2017-10-03 12:46:18 -0700
commit57f646bef272ba86e16a8d4814006cfd2b40cdea (patch)
tree80a11e452b601d8e7cfd41f0ed6054d1884047f5
parent122c4c39e88ab24b4b80990128a47795161aa154 (diff)
parentc760744125ff18a850f0ac06fd16a9556f7120e9 (diff)
Merge "qcacmn: Add qdf memory domain support" into wlan-cmn.driver.lnx.1.0
-rw-r--r--qdf/inc/qdf_mem.h63
-rw-r--r--qdf/inc/qdf_types.h35
-rw-r--r--qdf/linux/src/i_qdf_trace.h2
-rw-r--r--qdf/linux/src/qdf_mem.c589
-rw-r--r--qdf/linux/src/qdf_trace.c49
5 files changed, 404 insertions, 334 deletions
diff --git a/qdf/inc/qdf_mem.h b/qdf/inc/qdf_mem.h
index 52899c7c1313..a95f759f4592 100644
--- a/qdf/inc/qdf_mem.h
+++ b/qdf/inc/qdf_mem.h
@@ -86,14 +86,71 @@ void qdf_mem_init(void);
*/
void qdf_mem_exit(void);
+/**
+ * struct qdf_mem_domain - memory domains for tracking memory allocations
+ * @QDF_MEM_DOMAIN_INIT: The default memory domain
+ * @QDF_MEM_DOMAIN_ACTIVE: The network interface active memory domain
+ * @QDF_MEM_DOMAIN_MAX_COUNT: The number of memory domains for iterating, etc.
+ */
+enum qdf_mem_domain {
+ QDF_MEM_DOMAIN_INIT,
+ QDF_MEM_DOMAIN_ACTIVE,
+ QDF_MEM_DOMAIN_MAX_COUNT,
+};
+
#ifdef MEMORY_DEBUG
#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_set_domain() - Set the current memory domain
+ * @domain: the domain to change to
+ *
+ * This function changes the memory domain associated with future memory
+ * allocations. This can be used to check for memory leaks when combined with
+ * qdf_mem_check_for_leaks().
+ *
+ * Return: None
+ */
+void qdf_mem_set_domain(enum qdf_mem_domain domain);
+
+/**
+ * qdf_mem_check_for_leaks() - Assert that the current memory domain is empty
+ *
+ * Call this to ensure there are no active memory allocations being tracked
+ * against the current memory domain. For example, one should call this function
+ * immediately before a call to qdf_mem_set_domain() as a memory leak detection
+ * mechanism.
+ *
+ * e.g.
+ * qdf_mem_set_domain(QDF_MEM_DOMAIN_ACTIVE);
+ *
+ * ...
+ *
+ * // memory is allocated and freed
+ *
+ * ...
+ *
+ * // before transitioning back to inactive state,
+ * // make sure all active memory has been freed
+ * qdf_mem_check_for_leaks();
+ * qdf_mem_set_domain(QDF_MEM_DOMAIN_INIT);
+ *
+ * ...
+ *
+ * // before program exit, make sure init time memory is freed
+ * qdf_mem_check_for_leaks();
+ * exit();
+ *
+ * Return: None
+ */
+void qdf_mem_check_for_leaks(void);
#else
-void *
-qdf_mem_malloc(qdf_size_t size);
-#endif
+void *qdf_mem_malloc(qdf_size_t size);
+static inline void qdf_mem_set_domain(enum qdf_mem_domain domain) { }
+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);
diff --git a/qdf/inc/qdf_types.h b/qdf/inc/qdf_types.h
index 9e695137ac60..07e8bf99231d 100644
--- a/qdf/inc/qdf_types.h
+++ b/qdf/inc/qdf_types.h
@@ -413,15 +413,44 @@ enum tQDF_GLOBAL_CON_MODE {
void __printf(3, 4) qdf_trace_msg(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
char *str_format, ...);
+/**
+ * qdf_vtrace_msg() - the va_list version of qdf_trace_msg
+ * @module: the calling module's Id
+ * @level: the logging level to log using
+ * @str_format: the log format string
+ * @val: the va_list containing the values to format according to str_format
+ *
+ * Return: None
+ */
+void qdf_vtrace_msg(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
+ char *str_format, va_list val);
+
#define qdf_print(args...) \
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, ## args)
-#define qdf_debug(args...) \
- QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_DEBUG, ## args)
+#define qdf_logfl(level, format, args...) \
+ QDF_TRACE(QDF_MODULE_ID_QDF, level, FL(format), ## args)
+
+#define qdf_alert(format, args...) \
+ qdf_logfl(QDF_TRACE_LEVEL_FATAL, format, ## args)
+#define qdf_err(format, args...) \
+ qdf_logfl(QDF_TRACE_LEVEL_ERROR, format, ## args)
+#define qdf_warn(format, args...) \
+ qdf_logfl(QDF_TRACE_LEVEL_WARN, format, ## args)
+#define qdf_info(format, args...) \
+ qdf_logfl(QDF_TRACE_LEVEL_INFO, format, ## args)
+#define qdf_debug(format, args...) \
+ qdf_logfl(QDF_TRACE_LEVEL_DEBUG, format, ## args)
#else
-#define qdf_debug printk
+
#define qdf_print printk
+#define qdf_alert printk
+#define qdf_err printk
+#define qdf_warn printk
+#define qdf_info printk
+#define qdf_debug printk
+
#endif /* CONFIG_MCL */
#define qdf_vprint __qdf_vprint
diff --git a/qdf/linux/src/i_qdf_trace.h b/qdf/linux/src/i_qdf_trace.h
index 787f560682d9..ee35df45f88e 100644
--- a/qdf/linux/src/i_qdf_trace.h
+++ b/qdf/linux/src/i_qdf_trace.h
@@ -56,6 +56,7 @@
*/
#if defined(WLAN_DEBUG) || defined(DEBUG)
#define QDF_TRACE qdf_trace_msg
+#define QDF_VTRACE qdf_vtrace_msg
#define QDF_TRACE_HEX_DUMP qdf_trace_hex_dump
#define QDF_TRACE_RATE_LIMITED(rate, module, level, format, ...)\
do {\
@@ -68,6 +69,7 @@
} while (0)
#else
#define QDF_TRACE(arg ...)
+#define QDF_VTRACE(arg ...)
#define QDF_TRACE_HEX_DUMP(arg ...)
#define QDF_TRACE_RATE_LIMITED(arg ...)
#endif
diff --git a/qdf/linux/src/qdf_mem.c b/qdf/linux/src/qdf_mem.c
index 5d8c3ec34e53..8fd826c3f734 100644
--- a/qdf/linux/src/qdf_mem.c
+++ b/qdf/linux/src/qdf_mem.c
@@ -58,14 +58,14 @@
#ifdef MEMORY_DEBUG
#include <qdf_list.h>
-static qdf_list_t qdf_mem_list;
-static qdf_list_t qdf_mem_trash_list;
+
+static enum qdf_mem_domain qdf_mem_current_domain;
+static qdf_list_t qdf_mem_domains[QDF_MEM_DOMAIN_MAX_COUNT];
+static qdf_list_t *qdf_mem_list;
static qdf_spinlock_t qdf_mem_list_lock;
-static uint8_t WLAN_MEM_HEADER[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
- 0x67, 0x68 };
-static uint8_t WLAN_MEM_TAIL[] = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
- 0x86, 0x87 };
+static uint64_t WLAN_MEM_HEADER = 0x6162636465666768;
+static uint64_t WLAN_MEM_TAIL = 0x8081828384858687;
/**
* struct s_qdf_mem_struct - memory object to dubug
@@ -79,11 +79,23 @@ static uint8_t WLAN_MEM_TAIL[] = { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
struct s_qdf_mem_struct {
qdf_list_node_t node;
char *file_name;
- unsigned int line_num;
- unsigned int size;
- uint8_t header[8];
- qdf_atomic_t in_use;
+ uint32_t line_num;
+ uint32_t size;
+ uint64_t header;
+ enum qdf_mem_domain domain;
};
+
+static const uint8_t *qdf_mem_domain_name(enum qdf_mem_domain domain)
+{
+ switch (domain) {
+ case QDF_MEM_DOMAIN_INIT:
+ return "Init";
+ case QDF_MEM_DOMAIN_ACTIVE:
+ return "Active";
+ default:
+ return "Invalid";
+ }
+}
#endif /* MEMORY_DEBUG */
/* Preprocessor Definitions and Constants */
@@ -97,10 +109,6 @@ u_int8_t prealloc_disabled = 1;
qdf_declare_param(prealloc_disabled, byte);
qdf_export_symbol(prealloc_disabled);
-
-
-
-
#if defined WLAN_DEBUGFS
/* Debugfs root directory for qdf_mem */
@@ -149,6 +157,30 @@ void qdf_mem_skb_dec(qdf_size_t size)
}
#ifdef MEMORY_DEBUG
+static int qdf_err_printer(void *priv, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ QDF_VTRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, (char *)fmt, args);
+ va_end(args);
+
+ return 0;
+}
+
+static int seq_printf_printer(void *priv, const char *fmt, ...)
+{
+ struct seq_file *file = priv;
+ va_list args;
+
+ va_start(args, fmt);
+ seq_vprintf(file, fmt, args);
+ seq_puts(file, "\n");
+ va_end(args);
+
+ return 0;
+}
+
/**
* struct __qdf_mem_info - memory statistics
* @file_name: the file which allocated memory
@@ -159,186 +191,134 @@ void qdf_mem_skb_dec(qdf_size_t size)
*/
struct __qdf_mem_info {
char *file_name;
- unsigned int line_num;
- unsigned int size;
- unsigned int count;
+ uint32_t line_num;
+ uint32_t size;
+ uint32_t count;
};
-
/*
- * A table to identify duplicates in close proximity. The table depth defines
- * the proximity scope. A deeper table takes more time. Chose any optimum value.
- *
+ * The table depth defines the de-duplication proximity scope.
+ * A deeper table takes more time, so choose any optimum value.
*/
-#define QDF_MEM_STAT_TABLE_SIZE 4
-static struct __qdf_mem_info qdf_mem_info_table[QDF_MEM_STAT_TABLE_SIZE];
-
-
+#define QDF_MEM_STAT_TABLE_SIZE 8
/**
- * qdf_mem_info_table_init() - initialize the stat table
+ * qdf_mem_domain_print_header() - memory domain header print logic
+ * @print: the print adapter function
+ * @print_priv: the private data to be consumed by @print
*
* Return: None
*/
-static void qdf_mem_info_table_init(void)
+static void qdf_mem_domain_print_header(qdf_abstract_print print,
+ void *print_priv)
{
- memset(&qdf_mem_info_table, 0, sizeof(qdf_mem_info_table));
+ print(print_priv,
+ "--------------------------------------------------------------");
+ print(print_priv, " count size total filename");
+ print(print_priv,
+ "--------------------------------------------------------------");
}
/**
- * qdf_mem_get_node() - increase the node usage count
- * @n: node
- *
- * An increased usage count will block the memory from getting released.
- * Initially the usage count is incremented from qdf_mem_malloc_debug().
- * Corresponding qdf_mem_free() will decrement the reference count and frees up
- * the memory when the usage count reaches zero. Here decrement and test is an
- * atomic operation in qdf_mem_free() to avoid any race condition.
- *
- * If a caller wants to take the ownership of an allocated memory, it can call
- * this function with the associated node.
+ * qdf_mem_meta_table_print() - memory metadata table print logic
+ * @table: the memory metadata table to print
+ * @print: the print adapter function
+ * @print_priv: the private data to be consumed by @print
*
* Return: None
- *
*/
-static void qdf_mem_get_node(qdf_list_node_t *n)
+static void qdf_mem_meta_table_print(struct __qdf_mem_info *table,
+ qdf_abstract_print print,
+ void *print_priv)
{
- struct s_qdf_mem_struct *m = container_of(n, typeof(*m), node);
-
- qdf_atomic_inc(&m->in_use);
-}
-
-/**
- * qdf_mem_put_node_free() - decrease the node usage count and free memory
- * @n: node
- *
- * Additionally it releases the memory when the usage count reaches zero. Usage
- * count is decremented and tested against zero in qdf_mem_free(). If the count
- * is 0, the node and associated memory gets freed.
- *
- * Return: None
- *
- */
-static void qdf_mem_put_node_free(qdf_list_node_t *n)
-{
- struct s_qdf_mem_struct *m = container_of(n, typeof(*m), node);
-
- /* qdf_mem_free() is expecting the same address returned by
- * qdf_mem_malloc_debug(), which is 'm + sizeof(s_qdf_mem_struct)' */
- qdf_mem_free(m + 1);
-}
-
-/**
- * qdf_mem_get_first() - get the first node.
- *
- * Return: node
- */
-static qdf_list_node_t *qdf_mem_get_first(void)
-{
- QDF_STATUS status;
- qdf_list_node_t *node = NULL;
-
- qdf_spin_lock_bh(&qdf_mem_list_lock);
- status = qdf_list_peek_front(&qdf_mem_list, &node);
- if (QDF_STATUS_SUCCESS == status)
- qdf_mem_get_node(node);
- qdf_spin_unlock_bh(&qdf_mem_list_lock);
-
- return node;
-}
-
-/**
- * qdf_mem_get_next() - get the next node
- * @n: node
- *
- * Return: next node
- */
-static qdf_list_node_t *qdf_mem_get_next(qdf_list_node_t *n)
-{
- QDF_STATUS status;
- qdf_list_node_t *node = NULL;
-
- qdf_spin_lock_bh(&qdf_mem_list_lock);
- status = qdf_list_peek_next(&qdf_mem_list, n, &node);
- if (QDF_STATUS_SUCCESS == status)
- qdf_mem_get_node(node);
-
- qdf_spin_unlock_bh(&qdf_mem_list_lock);
-
- qdf_mem_put_node_free(n);
-
- return node;
+ int i;
-}
+ for (i = 0; i < QDF_MEM_STAT_TABLE_SIZE; i++) {
+ if (!table[i].count)
+ break;
-static void qdf_mem_seq_print_header(struct seq_file *seq)
-{
- seq_puts(seq, "\n");
- seq_puts(seq, "filename line size x no [ total ]\n");
- seq_puts(seq, "\n");
+ print(print_priv,
+ "%6u x %5u = %7uB @ %s:%u",
+ table[i].count,
+ table[i].size,
+ table[i].count * table[i].size,
+ kbasename(table[i].file_name),
+ table[i].line_num);
+ }
}
/**
- * qdf_mem_info_table_insert() - insert node into an array
- * @n: node
+ * qdf_mem_meta_table_insert() - insert memory metadata into the given table
+ * @table: the memory metadata table to insert into
+ * @meta: the memory metadata to insert
*
- * Return:
- * true - success
- * false - failure
+ * Return: true if the table is full after inserting, false otherwise
*/
-static bool qdf_mem_info_table_insert(qdf_list_node_t *n)
+static bool qdf_mem_meta_table_insert(struct __qdf_mem_info *table,
+ struct s_qdf_mem_struct *meta)
{
int i;
- struct __qdf_mem_info *t = qdf_mem_info_table;
- bool dup;
- bool consumed;
- struct s_qdf_mem_struct *m = (struct s_qdf_mem_struct *)n;
for (i = 0; i < QDF_MEM_STAT_TABLE_SIZE; i++) {
- if (!t[i].count) {
- t[i].file_name = m->file_name;
- t[i].line_num = m->line_num;
- t[i].size = m->size;
- t[i].count++;
+ if (!table[i].count) {
+ table[i].file_name = meta->file_name;
+ table[i].line_num = meta->line_num;
+ table[i].size = meta->size;
+ table[i].count = 1;
break;
}
- dup = !strcmp(t[i].file_name, m->file_name) &&
- (t[i].line_num == m->line_num) &&
- (t[i].size == m->size);
- if (dup) {
- t[i].count++;
+
+ if (table[i].file_name == meta->file_name &&
+ table[i].line_num == meta->line_num &&
+ table[i].size == meta->size) {
+ table[i].count++;
break;
}
}
- consumed = (i < QDF_MEM_STAT_TABLE_SIZE);
-
- return consumed;
+ /* return true if the table is now full */
+ return i >= QDF_MEM_STAT_TABLE_SIZE - 1;
}
/**
- * qdf_mem_seq_print() - print the table using seq_printf
- * @seq: seq_file handle
- *
- * Node table will be cleared once printed.
+ * qdf_mem_domain_print() - output agnostic memory domain print logic
+ * @domain: the memory domain to print
+ * @print: the print adapter function
+ * @print_priv: the private data to be consumed by @print
*
* Return: None
*/
-static void qdf_mem_seq_print(struct seq_file *seq)
+static void qdf_mem_domain_print(qdf_list_t *domain,
+ qdf_abstract_print print,
+ void *print_priv)
{
- int i;
- struct __qdf_mem_info *t = qdf_mem_info_table;
-
- for (i = 0; i < QDF_MEM_STAT_TABLE_SIZE && t[i].count; i++) {
- seq_printf(seq,
- "%-35s%6d\t%6d x %4d\t[%7d]\n",
- kbasename(t[i].file_name),
- t[i].line_num, t[i].size,
- t[i].count,
- t[i].size * t[i].count);
+ QDF_STATUS status;
+ struct __qdf_mem_info table[QDF_MEM_STAT_TABLE_SIZE];
+ qdf_list_node_t *node;
+
+ qdf_mem_zero(table, sizeof(table));
+ qdf_mem_domain_print_header(print, print_priv);
+
+ /* hold lock while inserting to avoid use-after free of the metadata */
+ 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;
+ bool is_full = qdf_mem_meta_table_insert(table, meta);
+
+ qdf_spin_unlock(&qdf_mem_list_lock);
+
+ if (is_full) {
+ qdf_mem_meta_table_print(table, print, print_priv);
+ qdf_mem_zero(table, sizeof(table));
+ }
+
+ qdf_spin_lock(&qdf_mem_list_lock);
+ status = qdf_list_peek_next(domain, node, &node);
}
+ qdf_spin_unlock(&qdf_mem_list_lock);
- qdf_mem_info_table_init();
+ qdf_mem_meta_table_print(table, print, print_priv);
}
/**
@@ -346,79 +326,58 @@ static void qdf_mem_seq_print(struct seq_file *seq)
* @seq: seq_file handle
* @pos: The start position of the sequence
*
- * Return:
- * SEQ_START_TOKEN - Prints header
- * None zero value - Node
- * NULL - End of the sequence
+ * Return: iterator pointer, or NULL if iteration is complete
*/
static void *qdf_mem_seq_start(struct seq_file *seq, loff_t *pos)
{
- if (*pos == 0) {
- qdf_mem_info_table_init();
- return SEQ_START_TOKEN;
- } else if (seq->private) {
- return qdf_mem_get_next(seq->private);
- }
+ if (*pos < 0 || *pos >= QDF_MEM_DOMAIN_MAX_COUNT)
+ return NULL;
- return NULL;
+ /* just use the current position as our iterator */
+ return pos;
}
/**
* qdf_mem_seq_next() - next sequential callback
- * @seq: seq_file handle
- * @v: the current iterator
- * @pos: the current position [not used]
+ * @seq: seq_file handle
+ * @v: the current iterator
+ * @pos: the current position
*
* Get the next node and release previous node.
*
- * Return:
- * None zero value - Next node
- * NULL - No more to process in the list
+ * Return: iterator pointer, or NULL if iteration is complete
*/
static void *qdf_mem_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
- qdf_list_node_t *node;
-
++*pos;
- if (v == SEQ_START_TOKEN)
- node = qdf_mem_get_first();
- else
- node = qdf_mem_get_next(v);
-
- return node;
+ return qdf_mem_seq_start(seq, pos);
}
/**
* qdf_mem_seq_stop() - stop sequential callback
- * @seq: seq_file handle
- * @v: current iterator
+ * @seq: seq_file handle
+ * @v: current iterator
*
- * Return: None
+ * Return: None
*/
-static void qdf_mem_seq_stop(struct seq_file *seq, void *v)
-{
- qdf_mem_seq_print(seq);
- seq->private = v;
-}
+static void qdf_mem_seq_stop(struct seq_file *seq, void *v) { }
/**
* qdf_mem_seq_show() - print sequential callback
- * @seq: seq_file handle
- * @v: current iterator
+ * @seq: seq_file handle
+ * @v: current iterator
*
* Return: 0 - success
*/
static int qdf_mem_seq_show(struct seq_file *seq, void *v)
{
+ enum qdf_mem_domain domain_id = *(enum qdf_mem_domain *)v;
- if (v == SEQ_START_TOKEN) {
- qdf_mem_seq_print_header(seq);
- return 0;
- }
-
- while (!qdf_mem_info_table_insert(v))
- qdf_mem_seq_print(seq);
+ seq_printf(seq, "\n%s Memory Domain (Id %d)\n",
+ qdf_mem_domain_name(domain_id), domain_id);
+ qdf_mem_domain_print(&qdf_mem_domains[domain_id],
+ seq_printf_printer, seq);
return 0;
}
@@ -824,28 +783,45 @@ static inline bool qdf_mem_prealloc_put(void *ptr)
*/
static void qdf_mem_debug_init(void)
{
+ int i;
+
/* Initalizing the list with maximum size of 60000 */
- qdf_list_create(&qdf_mem_list, 60000);
- qdf_list_create(&qdf_mem_trash_list, 60000);
+ qdf_mem_current_domain = QDF_MEM_DOMAIN_INIT;
+ qdf_mem_list = &qdf_mem_domains[QDF_MEM_DOMAIN_INIT];
+ for (i = 0; i < QDF_MEM_DOMAIN_MAX_COUNT; ++i)
+ qdf_list_create(&qdf_mem_domains[i], 60000);
qdf_spinlock_create(&qdf_mem_list_lock);
qdf_net_buf_debug_init();
}
#ifdef CONFIG_HALT_KMEMLEAK
-static void qdf_handle_leaked_memory(qdf_list_node_t *node)
+/*
+ * There are two scenarios for handling memory leaks. We want to either:
+ * 1) Crash and not release memory for offline debugging (internal testing)
+ * 2) Clean up any leaks and continue (production devices)
+ */
+
+static inline void qdf_mem_leak_panic(void)
{
- /* do not free the leaked memory if halt on memleak is enabled
- * such that leaked memory does not get poisoned and can be
- * used for offline debugging
- */
- qdf_spin_lock(&qdf_mem_list_lock);
- qdf_list_insert_front(&qdf_mem_trash_list, node);
- qdf_spin_unlock(&qdf_mem_list_lock);
+ QDF_BUG(0);
}
+
+static inline void qdf_mem_free_leaked_memory(qdf_list_t *domain) { }
#else
-static void qdf_handle_leaked_memory(qdf_list_node_t *node)
+static inline void qdf_mem_leak_panic(void) { }
+
+static void qdf_mem_free_leaked_memory(qdf_list_t *domain)
{
- kfree((void *)node);
+ QDF_STATUS status;
+ qdf_list_node_t *node;
+
+ qdf_spin_lock(&qdf_mem_list_lock);
+ status = qdf_list_remove_front(domain, &node);
+ while (QDF_IS_STATUS_SUCCESS(status)) {
+ kfree(node);
+ status = qdf_list_remove_front(domain, &node);
+ }
+ qdf_spin_unlock(&qdf_mem_list_lock);
}
#endif
@@ -857,68 +833,30 @@ static void qdf_handle_leaked_memory(qdf_list_node_t *node)
*/
static void qdf_mem_debug_clean(void)
{
- uint32_t list_size;
-
- list_size = qdf_list_size(&qdf_mem_list);
- if (list_size) {
- qdf_list_node_t *node;
- QDF_STATUS qdf_status;
-
- struct s_qdf_mem_struct *mem_struct;
- char *prev_mleak_file = "";
- unsigned int prev_mleak_line_num = 0;
- unsigned int prev_mleak_sz = 0;
- unsigned int mleak_cnt = 0;
-
- QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO,
- "%s: List is not Empty. list_size %d ",
- __func__, (int)list_size);
-
- do {
- qdf_spin_lock(&qdf_mem_list_lock);
- qdf_status =
- qdf_list_remove_front(&qdf_mem_list, &node);
- qdf_spin_unlock(&qdf_mem_list_lock);
- if (QDF_STATUS_SUCCESS == qdf_status) {
- mem_struct = (struct s_qdf_mem_struct *)node;
- /* Take care to log only once multiple memory
- * leaks from the same place
- */
- if (strcmp(prev_mleak_file,
- mem_struct->file_name)
- || (prev_mleak_line_num !=
- mem_struct->line_num)
- || (prev_mleak_sz != mem_struct->size)) {
- if (mleak_cnt != 0) {
- QDF_TRACE(QDF_MODULE_ID_QDF,
- QDF_TRACE_LEVEL_FATAL,
- "%d Time Memory Leak@ File %s, @Line %d, size %d",
- mleak_cnt,
- prev_mleak_file,
- prev_mleak_line_num,
- prev_mleak_sz);
- }
- prev_mleak_file = mem_struct->file_name;
- prev_mleak_line_num =
- mem_struct->line_num;
- prev_mleak_sz = mem_struct->size;
- mleak_cnt = 0;
- }
- mleak_cnt++;
- qdf_handle_leaked_memory(node);
- }
- } while (qdf_status == QDF_STATUS_SUCCESS);
-
- /* Print last memory leak from the module */
- if (mleak_cnt) {
- QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
- "%d Time memory Leak@ File %s, @Line %d, size %d",
- mleak_cnt, prev_mleak_file,
- prev_mleak_line_num, prev_mleak_sz);
- }
-#ifdef CONFIG_HALT_KMEMLEAK
- QDF_BUG(0);
-#endif
+ bool leaks_detected = false;
+ int i;
+
+ /* detect and print leaks */
+ for (i = 0; i < QDF_MEM_DOMAIN_MAX_COUNT; ++i) {
+ qdf_list_t *domain = &qdf_mem_domains[i];
+
+ if (qdf_list_empty(domain))
+ continue;
+
+ leaks_detected = true;
+
+ qdf_err("\nMemory leaks detected in the %s (Id %d) domain!\n\n",
+ qdf_mem_domain_name(i), i);
+ qdf_mem_domain_print(domain, qdf_err_printer, NULL);
+ }
+
+ if (leaks_detected) {
+ /* panic, if enabled */
+ qdf_mem_leak_panic();
+
+ /* if we didn't crash, release the leaked memory */
+ for (i = 0; i < QDF_MEM_DOMAIN_MAX_COUNT; ++i)
+ qdf_mem_free_leaked_memory(&qdf_mem_domains[i]);
}
}
@@ -929,9 +867,14 @@ static void qdf_mem_debug_clean(void)
*/
static void qdf_mem_debug_exit(void)
{
+ int i;
+
qdf_net_buf_debug_exit();
qdf_mem_debug_clean();
- qdf_list_destroy(&qdf_mem_list);
+
+ for (i = 0; i < QDF_MEM_DOMAIN_MAX_COUNT; ++i)
+ qdf_list_destroy(&qdf_mem_domains[i]);
+
qdf_spinlock_destroy(&qdf_mem_list_lock);
}
@@ -950,14 +893,13 @@ static void qdf_mem_debug_exit(void)
* 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, char *file_name, uint32_t line_num)
{
struct s_qdf_mem_struct *mem_struct;
void *mem_ptr = NULL;
uint32_t new_size;
int flags = GFP_KERNEL;
- unsigned long time_before_kzalloc;
+ unsigned long time_before_kzalloc;
if (size > (1024 * 1024) || size == 0) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
@@ -973,9 +915,9 @@ void *qdf_mem_malloc_debug(size_t size,
if (in_interrupt() || irqs_disabled() || in_atomic())
flags = GFP_ATOMIC;
- new_size = size + sizeof(struct s_qdf_mem_struct) + 8;/*TBD: what is 8*/
+ new_size = sizeof(*mem_struct) + size + sizeof(WLAN_MEM_TAIL);
time_before_kzalloc = qdf_mc_timer_get_system_time();
- mem_struct = (struct s_qdf_mem_struct *)kzalloc(new_size, flags);
+ mem_struct = kzalloc(new_size, flags);
/**
* If time taken by kmalloc is greater than
* QDF_GET_MEMORY_TIME_THRESHOLD msec
@@ -988,32 +930,27 @@ void *qdf_mem_malloc_debug(size_t size,
qdf_mc_timer_get_system_time() - time_before_kzalloc,
size, (void *)_RET_IP_, line_num);
- if (mem_struct != NULL) {
- QDF_STATUS qdf_status;
+ 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;
- qdf_atomic_inc(&mem_struct->in_use);
- qdf_mem_kmalloc_inc(ksize(mem_struct));
-
- qdf_mem_copy(&mem_struct->header[0],
- &WLAN_MEM_HEADER[0], sizeof(WLAN_MEM_HEADER));
+ mem_struct->domain = qdf_mem_current_domain;
+ mem_struct->header = WLAN_MEM_HEADER;
+ *trailer = WLAN_MEM_TAIL;
- qdf_mem_copy((uint8_t *) (mem_struct + 1) + size,
- &WLAN_MEM_TAIL[0], sizeof(WLAN_MEM_TAIL));
+ qdf_mem_kmalloc_inc(ksize(mem_struct));
qdf_spin_lock_irqsave(&qdf_mem_list_lock);
- qdf_status = qdf_list_insert_front(&qdf_mem_list,
- &mem_struct->node);
+ status = qdf_list_insert_front(qdf_mem_list, &mem_struct->node);
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
- if (QDF_STATUS_SUCCESS != qdf_status) {
- QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
- "%s: Unable to insert node into List qdf_status %d",
- __func__, qdf_status);
- }
-
- mem_ptr = (void *)(mem_struct + 1);
+ if (QDF_IS_STATUS_ERROR(status))
+ qdf_err("Unable to insert into list status %d", status);
}
return mem_ptr;
@@ -1068,11 +1005,15 @@ static bool qdf_mem_validate_node_for_free(qdf_list_node_t *qdf_node)
void qdf_mem_free(void *ptr)
{
struct s_qdf_mem_struct *mem_struct;
+ uint64_t *trailer;
/* freeing a null pointer is valid */
if (qdf_unlikely(ptr == NULL))
return;
+ if (qdf_mem_prealloc_put(ptr))
+ return;
+
mem_struct = ((struct s_qdf_mem_struct *)ptr) - 1;
if (qdf_unlikely(mem_struct == NULL)) {
@@ -1081,9 +1022,7 @@ void qdf_mem_free(void *ptr)
QDF_BUG(0);
}
- if (qdf_mem_prealloc_put(ptr))
- return;
-
+ trailer = (uint64_t *)(ptr + mem_struct->size);
qdf_spin_lock_irqsave(&qdf_mem_list_lock);
@@ -1092,8 +1031,7 @@ void qdf_mem_free(void *ptr)
* would be a use after free and would indicate a double free
* or invalid pointer passed.
*/
- if (qdf_mem_cmp(mem_struct->header, &WLAN_MEM_HEADER[0],
- sizeof(WLAN_MEM_HEADER)))
+ if (mem_struct->header != WLAN_MEM_HEADER)
goto error;
/*
@@ -1109,13 +1047,21 @@ void qdf_mem_free(void *ptr)
* It is unlikely that the above checks would pass in a
* double free case.
*/
- if (qdf_mem_cmp((uint8_t *) ptr + mem_struct->size,
- &WLAN_MEM_TAIL[0], sizeof(WLAN_MEM_TAIL)))
+ if (*trailer != WLAN_MEM_TAIL)
goto error;
- if (!qdf_atomic_dec_and_test(&mem_struct->in_use)) {
+ /* make sure the memory belongs to the current domain */
+ if (mem_struct->domain != qdf_mem_current_domain) {
qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
- return;
+ qdf_err("Memory domain mismatch; found %s, expected %s (%s:%u)",
+ qdf_mem_domain_name(mem_struct->domain),
+ qdf_mem_domain_name(qdf_mem_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);
}
/*
@@ -1123,14 +1069,15 @@ void qdf_mem_free(void *ptr)
* The empty list check will guarantee that we avoid a race condition.
*/
list_del_init(&mem_struct->node);
- qdf_mem_list.count--;
- qdf_spin_unlock_irqrestore(&qdf_mem_list_lock);
+ qdf_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(&qdf_mem_list, &mem_struct->node)) {
+ if (!qdf_list_has_node(qdf_mem_list, &mem_struct->node)) {
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
"%s: Unallocated memory (double free?)",
__func__);
@@ -1138,8 +1085,7 @@ error:
QDF_BUG(0);
}
- if (qdf_mem_cmp(mem_struct->header, &WLAN_MEM_HEADER[0],
- sizeof(WLAN_MEM_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);
@@ -1153,8 +1099,7 @@ error:
QDF_BUG(0);
}
- if (qdf_mem_cmp((uint8_t *) ptr + mem_struct->size,
- &WLAN_MEM_TAIL[0], sizeof(WLAN_MEM_TAIL))) {
+ 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);
@@ -1168,6 +1113,36 @@ error:
QDF_BUG(0);
}
qdf_export_symbol(qdf_mem_free);
+
+void qdf_mem_set_domain(enum qdf_mem_domain domain)
+{
+ QDF_BUG(domain >= QDF_MEM_DOMAIN_INIT);
+ if (domain < QDF_MEM_DOMAIN_INIT)
+ return;
+
+ QDF_BUG(domain < QDF_MEM_DOMAIN_MAX_COUNT);
+ if (domain >= QDF_MEM_DOMAIN_MAX_COUNT)
+ return;
+
+ qdf_spin_lock(&qdf_mem_list_lock);
+ qdf_mem_current_domain = domain;
+ qdf_mem_list = &qdf_mem_domains[domain];
+ qdf_spin_unlock(&qdf_mem_list_lock);
+}
+
+void qdf_mem_check_for_leaks(void)
+{
+ /* get local copy in case qdf_mem_list changes after empty check */
+ qdf_list_t *domain = qdf_mem_list;
+
+ if (!qdf_list_empty(domain)) {
+ qdf_err("Memory leaks detected in %s domain!",
+ qdf_mem_domain_name(qdf_mem_current_domain));
+ qdf_mem_domain_print(domain, qdf_err_printer, NULL);
+ qdf_mem_leak_panic();
+ }
+}
+
#else
static void qdf_mem_debug_init(void) {}
diff --git a/qdf/linux/src/qdf_trace.c b/qdf/linux/src/qdf_trace.c
index 53799d42b2c8..db49afdf205d 100644
--- a/qdf/linux/src/qdf_trace.c
+++ b/qdf/linux/src/qdf_trace.c
@@ -289,24 +289,8 @@ void qdf_snprintf(char *str_buffer, unsigned int size, char *str_format, ...)
qdf_export_symbol(qdf_snprintf);
#ifdef QDF_ENABLE_TRACING
-
-/**
- * qdf_trace_msg() - externally called trace function
- * @module: Module identifier a member of the QDF_MODULE_ID
- * enumeration that identifies the module issuing the trace message.
- * @level: Trace level a member of the QDF_TRACE_LEVEL enumeration
- * indicating the severity of the condition causing the trace message
- * to be issued. More severe conditions are more likely to be logged.
- * @str_format: Format string in which the message to be logged. This format
- * string contains printf-like replacement parameters, which follow
- * this parameter in the variable argument list.
- *
- * Checks the level of severity and accordingly prints the trace messages
- *
- * Return: None
- */
-void qdf_trace_msg(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
- char *str_format, ...)
+void qdf_vtrace_msg(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
+ char *str_format, va_list val)
{
char str_buffer[QDF_TRACE_BUFFER_SIZE];
int n;
@@ -325,9 +309,7 @@ void qdf_trace_msg(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
*/
static const char * const TRACE_LEVEL_STR[] = { " ", "F ",
"E ", "W ", "I ", "IH", "IM", "IL", "D" };
- va_list val;
- va_start(val, str_format);
/* print the prefix string into the string buffer... */
n = snprintf(str_buffer, QDF_TRACE_BUFFER_SIZE,
"wlan: [%d:%2s:%3s] ",
@@ -346,9 +328,34 @@ void qdf_trace_msg(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
pr_err("%s\n", str_buffer);
#endif
}
- va_end(val);
}
}
+qdf_export_symbol(qdf_vtrace_msg);
+
+/**
+ * qdf_trace_msg() - externally called trace function
+ * @module: Module identifier a member of the QDF_MODULE_ID
+ * enumeration that identifies the module issuing the trace message.
+ * @level: Trace level a member of the QDF_TRACE_LEVEL enumeration
+ * indicating the severity of the condition causing the trace message
+ * to be issued. More severe conditions are more likely to be logged.
+ * @str_format: Format string in which the message to be logged. This format
+ * string contains printf-like replacement parameters, which follow
+ * this parameter in the variable argument list.
+ *
+ * Checks the level of severity and accordingly prints the trace messages
+ *
+ * Return: None
+ */
+void qdf_trace_msg(QDF_MODULE_ID module, QDF_TRACE_LEVEL level,
+ char *str_format, ...)
+{
+ va_list val;
+
+ va_start(val, str_format);
+ qdf_vtrace_msg(module, level, str_format, val);
+ va_end(val);
+}
qdf_export_symbol(qdf_trace_msg);
/**