diff options
| author | CNSS_WLAN Service <cnssbldsw@qualcomm.com> | 2017-10-03 12:46:18 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-10-03 12:46:18 -0700 |
| commit | 57f646bef272ba86e16a8d4814006cfd2b40cdea (patch) | |
| tree | 80a11e452b601d8e7cfd41f0ed6054d1884047f5 | |
| parent | 122c4c39e88ab24b4b80990128a47795161aa154 (diff) | |
| parent | c760744125ff18a850f0ac06fd16a9556f7120e9 (diff) | |
Merge "qcacmn: Add qdf memory domain support" into wlan-cmn.driver.lnx.1.0
| -rw-r--r-- | qdf/inc/qdf_mem.h | 63 | ||||
| -rw-r--r-- | qdf/inc/qdf_types.h | 35 | ||||
| -rw-r--r-- | qdf/linux/src/i_qdf_trace.h | 2 | ||||
| -rw-r--r-- | qdf/linux/src/qdf_mem.c | 589 | ||||
| -rw-r--r-- | qdf/linux/src/qdf_trace.c | 49 |
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); /** |
