|
@@ -59,6 +59,39 @@ static bool is_initial_mem_debug_disabled;
|
|
#include "qdf_debug_domain.h"
|
|
#include "qdf_debug_domain.h"
|
|
#include <qdf_list.h>
|
|
#include <qdf_list.h>
|
|
|
|
|
|
|
|
+enum list_type {
|
|
|
|
+ LIST_TYPE_MEM = 0,
|
|
|
|
+ LIST_TYPE_DMA = 1,
|
|
|
|
+ LIST_TYPE_MAX,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * major_alloc_priv: private data registered to debugfs entry created to list
|
|
|
|
+ * the list major allocations
|
|
|
|
+ * @type: type of the list to be parsed
|
|
|
|
+ * @threshold: configured by user by overwriting the respective debugfs
|
|
|
|
+ * sys entry. This is to list the functions which requested
|
|
|
|
+ * memory/dma allocations more than threshold nubmer of times.
|
|
|
|
+ */
|
|
|
|
+struct major_alloc_priv {
|
|
|
|
+ enum list_type type;
|
|
|
|
+ uint32_t threshold;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static struct major_alloc_priv mem_priv = {
|
|
|
|
+ /* List type set to mem */
|
|
|
|
+ LIST_TYPE_MEM,
|
|
|
|
+ /* initial threshold to list APIs which allocates mem >= 50 times */
|
|
|
|
+ 50
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static struct major_alloc_priv dma_priv = {
|
|
|
|
+ /* List type set to DMA */
|
|
|
|
+ LIST_TYPE_DMA,
|
|
|
|
+ /* initial threshold to list APIs which allocates dma >= 50 times */
|
|
|
|
+ 50
|
|
|
|
+};
|
|
|
|
+
|
|
static qdf_list_t qdf_mem_domains[QDF_DEBUG_DOMAIN_COUNT];
|
|
static qdf_list_t qdf_mem_domains[QDF_DEBUG_DOMAIN_COUNT];
|
|
static qdf_spinlock_t qdf_mem_list_lock;
|
|
static qdf_spinlock_t qdf_mem_list_lock;
|
|
|
|
|
|
@@ -353,15 +386,20 @@ struct __qdf_mem_info {
|
|
#define QDF_MEM_STAT_TABLE_SIZE 8
|
|
#define QDF_MEM_STAT_TABLE_SIZE 8
|
|
|
|
|
|
/**
|
|
/**
|
|
- * qdf_mem_domain_print_header() - memory domain header print logic
|
|
|
|
|
|
+ * qdf_mem_debug_print_header() - memory debug header print logic
|
|
* @print: the print adapter function
|
|
* @print: the print adapter function
|
|
* @print_priv: the private data to be consumed by @print
|
|
* @print_priv: the private data to be consumed by @print
|
|
|
|
+ * @threshold: the threshold value set by user to list top allocations
|
|
*
|
|
*
|
|
* Return: None
|
|
* Return: None
|
|
*/
|
|
*/
|
|
-static void qdf_mem_domain_print_header(qdf_abstract_print print,
|
|
|
|
- void *print_priv)
|
|
|
|
|
|
+static void qdf_mem_debug_print_header(qdf_abstract_print print,
|
|
|
|
+ void *print_priv,
|
|
|
|
+ uint32_t threshold)
|
|
{
|
|
{
|
|
|
|
+ if (threshold)
|
|
|
|
+ print(print_priv, "APIs requested allocations >= %u no of time",
|
|
|
|
+ threshold);
|
|
print(print_priv,
|
|
print(print_priv,
|
|
"--------------------------------------------------------------");
|
|
"--------------------------------------------------------------");
|
|
print(print_priv,
|
|
print(print_priv,
|
|
@@ -375,12 +413,14 @@ static void qdf_mem_domain_print_header(qdf_abstract_print print,
|
|
* @table: the memory metadata table to print
|
|
* @table: the memory metadata table to print
|
|
* @print: the print adapter function
|
|
* @print: the print adapter function
|
|
* @print_priv: the private data to be consumed by @print
|
|
* @print_priv: the private data to be consumed by @print
|
|
|
|
+ * @threshold: the threshold value set by user to list top allocations
|
|
*
|
|
*
|
|
* Return: None
|
|
* Return: None
|
|
*/
|
|
*/
|
|
static void qdf_mem_meta_table_print(struct __qdf_mem_info *table,
|
|
static void qdf_mem_meta_table_print(struct __qdf_mem_info *table,
|
|
qdf_abstract_print print,
|
|
qdf_abstract_print print,
|
|
- void *print_priv)
|
|
|
|
|
|
+ void *print_priv,
|
|
|
|
+ uint32_t threshold)
|
|
{
|
|
{
|
|
int i;
|
|
int i;
|
|
char debug_str[QDF_DEBUG_STRING_SIZE];
|
|
char debug_str[QDF_DEBUG_STRING_SIZE];
|
|
@@ -412,6 +452,37 @@ static void qdf_mem_meta_table_print(struct __qdf_mem_info *table,
|
|
print(print_priv, "%s", debug_str);
|
|
print(print_priv, "%s", debug_str);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * qdf_print_major_alloc() - 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
|
|
|
|
+ * @threshold: the threshold value set by uset to list top allocations
|
|
|
|
+ *
|
|
|
|
+ * Return: None
|
|
|
|
+ */
|
|
|
|
+static void qdf_print_major_alloc(struct __qdf_mem_info *table,
|
|
|
|
+ qdf_abstract_print print,
|
|
|
|
+ void *print_priv,
|
|
|
|
+ uint32_t threshold)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < QDF_MEM_STAT_TABLE_SIZE; i++) {
|
|
|
|
+ if (!table[i].count)
|
|
|
|
+ break;
|
|
|
|
+ if (table[i].count >= threshold)
|
|
|
|
+ print(print_priv,
|
|
|
|
+ "%6u x %5u = %7uB @ %s:%u %pS %llu",
|
|
|
|
+ table[i].count,
|
|
|
|
+ table[i].size,
|
|
|
|
+ table[i].count * table[i].size,
|
|
|
|
+ table[i].func,
|
|
|
|
+ table[i].line, table[i].caller,
|
|
|
|
+ table[i].time);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* qdf_mem_meta_table_insert() - insert memory metadata into the given table
|
|
* qdf_mem_meta_table_insert() - insert memory metadata into the given table
|
|
* @table: the memory metadata table to insert into
|
|
* @table: the memory metadata table to insert into
|
|
@@ -454,19 +525,25 @@ static bool qdf_mem_meta_table_insert(struct __qdf_mem_info *table,
|
|
* @domain: the memory domain to print
|
|
* @domain: the memory domain to print
|
|
* @print: the print adapter function
|
|
* @print: the print adapter function
|
|
* @print_priv: the private data to be consumed by @print
|
|
* @print_priv: the private data to be consumed by @print
|
|
|
|
+ * @threshold: the threshold value set by uset to list top allocations
|
|
|
|
+ * @mem_print: pointer to function which prints the memory allocation data
|
|
*
|
|
*
|
|
* Return: None
|
|
* Return: None
|
|
*/
|
|
*/
|
|
static void qdf_mem_domain_print(qdf_list_t *domain,
|
|
static void qdf_mem_domain_print(qdf_list_t *domain,
|
|
qdf_abstract_print print,
|
|
qdf_abstract_print print,
|
|
- void *print_priv)
|
|
|
|
|
|
+ void *print_priv,
|
|
|
|
+ uint32_t threshold,
|
|
|
|
+ void (*mem_print)(struct __qdf_mem_info *,
|
|
|
|
+ qdf_abstract_print,
|
|
|
|
+ void *, uint32_t))
|
|
{
|
|
{
|
|
QDF_STATUS status;
|
|
QDF_STATUS status;
|
|
struct __qdf_mem_info table[QDF_MEM_STAT_TABLE_SIZE];
|
|
struct __qdf_mem_info table[QDF_MEM_STAT_TABLE_SIZE];
|
|
qdf_list_node_t *node;
|
|
qdf_list_node_t *node;
|
|
|
|
|
|
qdf_mem_zero(table, sizeof(table));
|
|
qdf_mem_zero(table, sizeof(table));
|
|
- qdf_mem_domain_print_header(print, print_priv);
|
|
|
|
|
|
+ qdf_mem_debug_print_header(print, print_priv, threshold);
|
|
|
|
|
|
/* hold lock while inserting to avoid use-after free of the metadata */
|
|
/* hold lock while inserting to avoid use-after free of the metadata */
|
|
qdf_spin_lock(&qdf_mem_list_lock);
|
|
qdf_spin_lock(&qdf_mem_list_lock);
|
|
@@ -478,7 +555,7 @@ static void qdf_mem_domain_print(qdf_list_t *domain,
|
|
qdf_spin_unlock(&qdf_mem_list_lock);
|
|
qdf_spin_unlock(&qdf_mem_list_lock);
|
|
|
|
|
|
if (is_full) {
|
|
if (is_full) {
|
|
- qdf_mem_meta_table_print(table, print, print_priv);
|
|
|
|
|
|
+ (*mem_print)(table, print, print_priv, threshold);
|
|
qdf_mem_zero(table, sizeof(table));
|
|
qdf_mem_zero(table, sizeof(table));
|
|
}
|
|
}
|
|
|
|
|
|
@@ -487,7 +564,7 @@ static void qdf_mem_domain_print(qdf_list_t *domain,
|
|
}
|
|
}
|
|
qdf_spin_unlock(&qdf_mem_list_lock);
|
|
qdf_spin_unlock(&qdf_mem_list_lock);
|
|
|
|
|
|
- qdf_mem_meta_table_print(table, print, print_priv);
|
|
|
|
|
|
+ (*mem_print)(table, print, print_priv, threshold);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -548,7 +625,10 @@ static int qdf_mem_seq_show(struct seq_file *seq, void *v)
|
|
seq_printf(seq, "\n%s Memory Domain (Id %d)\n",
|
|
seq_printf(seq, "\n%s Memory Domain (Id %d)\n",
|
|
qdf_debug_domain_name(domain_id), domain_id);
|
|
qdf_debug_domain_name(domain_id), domain_id);
|
|
qdf_mem_domain_print(qdf_mem_list_get(domain_id),
|
|
qdf_mem_domain_print(qdf_mem_list_get(domain_id),
|
|
- seq_printf_printer, seq);
|
|
|
|
|
|
+ seq_printf_printer,
|
|
|
|
+ seq,
|
|
|
|
+ 0,
|
|
|
|
+ qdf_mem_meta_table_print);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -567,6 +647,99 @@ static int qdf_mem_debugfs_open(struct inode *inode, struct file *file)
|
|
return seq_open(file, &qdf_mem_seq_ops);
|
|
return seq_open(file, &qdf_mem_seq_ops);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * qdf_major_alloc_show() - print sequential callback
|
|
|
|
+ * @seq: seq_file handle
|
|
|
|
+ * @v: current iterator
|
|
|
|
+ *
|
|
|
|
+ * Return: 0 - success
|
|
|
|
+ */
|
|
|
|
+static int qdf_major_alloc_show(struct seq_file *seq, void *v)
|
|
|
|
+{
|
|
|
|
+ enum qdf_debug_domain domain_id = *(enum qdf_debug_domain *)v;
|
|
|
|
+ struct major_alloc_priv *priv;
|
|
|
|
+ qdf_list_t *list;
|
|
|
|
+
|
|
|
|
+ priv = (struct major_alloc_priv *)seq->private;
|
|
|
|
+ seq_printf(seq, "\n%s Memory Domain (Id %d)\n",
|
|
|
|
+ qdf_debug_domain_name(domain_id), domain_id);
|
|
|
|
+
|
|
|
|
+ switch (priv->type) {
|
|
|
|
+ case LIST_TYPE_MEM:
|
|
|
|
+ list = qdf_mem_list_get(domain_id);
|
|
|
|
+ break;
|
|
|
|
+ case LIST_TYPE_DMA:
|
|
|
|
+ list = qdf_mem_dma_list(domain_id);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ list = NULL;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (list)
|
|
|
|
+ qdf_mem_domain_print(list,
|
|
|
|
+ seq_printf_printer,
|
|
|
|
+ seq,
|
|
|
|
+ priv->threshold,
|
|
|
|
+ qdf_print_major_alloc);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* sequential file operation table created to track major allocs */
|
|
|
|
+static const struct seq_operations qdf_major_allocs_seq_ops = {
|
|
|
|
+ .start = qdf_mem_seq_start,
|
|
|
|
+ .next = qdf_mem_seq_next,
|
|
|
|
+ .stop = qdf_mem_seq_stop,
|
|
|
|
+ .show = qdf_major_alloc_show,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static int qdf_major_allocs_open(struct inode *inode, struct file *file)
|
|
|
|
+{
|
|
|
|
+ void *private = inode->i_private;
|
|
|
|
+ struct seq_file *seq;
|
|
|
|
+ int rc;
|
|
|
|
+
|
|
|
|
+ rc = seq_open(file, &qdf_major_allocs_seq_ops);
|
|
|
|
+ if (rc == 0) {
|
|
|
|
+ seq = file->private_data;
|
|
|
|
+ seq->private = private;
|
|
|
|
+ }
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static ssize_t qdf_major_alloc_set_threshold(struct file *file,
|
|
|
|
+ const char __user *user_buf,
|
|
|
|
+ size_t count,
|
|
|
|
+ loff_t *pos)
|
|
|
|
+{
|
|
|
|
+ char buf[32];
|
|
|
|
+ ssize_t buf_size;
|
|
|
|
+ uint32_t threshold;
|
|
|
|
+ struct seq_file *seq = file->private_data;
|
|
|
|
+ struct major_alloc_priv *priv = (struct major_alloc_priv *)seq->private;
|
|
|
|
+
|
|
|
|
+ buf_size = min(count, (sizeof(buf) - 1));
|
|
|
|
+ if (buf_size <= 0)
|
|
|
|
+ return 0;
|
|
|
|
+ if (copy_from_user(buf, user_buf, buf_size))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+ buf[buf_size] = '\0';
|
|
|
|
+ if (!kstrtou32(buf, 10, &threshold))
|
|
|
|
+ priv->threshold = threshold;
|
|
|
|
+ return buf_size;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* file operation table for listing major allocs */
|
|
|
|
+static const struct file_operations fops_qdf_major_allocs = {
|
|
|
|
+ .owner = THIS_MODULE,
|
|
|
|
+ .open = qdf_major_allocs_open,
|
|
|
|
+ .read = seq_read,
|
|
|
|
+ .llseek = seq_lseek,
|
|
|
|
+ .release = seq_release,
|
|
|
|
+ .write = qdf_major_alloc_set_threshold,
|
|
|
|
+};
|
|
|
|
+
|
|
/* debugfs file operation table */
|
|
/* debugfs file operation table */
|
|
static const struct file_operations fops_qdf_mem_debugfs = {
|
|
static const struct file_operations fops_qdf_mem_debugfs = {
|
|
.owner = THIS_MODULE,
|
|
.owner = THIS_MODULE,
|
|
@@ -590,6 +763,18 @@ static QDF_STATUS qdf_mem_debug_debugfs_init(void)
|
|
NULL,
|
|
NULL,
|
|
&fops_qdf_mem_debugfs);
|
|
&fops_qdf_mem_debugfs);
|
|
|
|
|
|
|
|
+ debugfs_create_file("major_mem_allocs",
|
|
|
|
+ 0600,
|
|
|
|
+ qdf_mem_debugfs_root,
|
|
|
|
+ &mem_priv,
|
|
|
|
+ &fops_qdf_major_allocs);
|
|
|
|
+
|
|
|
|
+ debugfs_create_file("major_dma_allocs",
|
|
|
|
+ 0600,
|
|
|
|
+ qdf_mem_debugfs_root,
|
|
|
|
+ &dma_priv,
|
|
|
|
+ &fops_qdf_major_allocs);
|
|
|
|
+
|
|
return QDF_STATUS_SUCCESS;
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -950,7 +1135,11 @@ qdf_mem_domain_check_for_leaks(enum qdf_debug_domain domain,
|
|
|
|
|
|
qdf_err("Memory leaks detected in %s domain!",
|
|
qdf_err("Memory leaks detected in %s domain!",
|
|
qdf_debug_domain_name(domain));
|
|
qdf_debug_domain_name(domain));
|
|
- qdf_mem_domain_print(mem_list, qdf_err_printer, NULL);
|
|
|
|
|
|
+ qdf_mem_domain_print(mem_list,
|
|
|
|
+ qdf_err_printer,
|
|
|
|
+ NULL,
|
|
|
|
+ 0,
|
|
|
|
+ qdf_mem_meta_table_print);
|
|
|
|
|
|
return mem_list->count;
|
|
return mem_list->count;
|
|
}
|
|
}
|