diff --git a/qdf/inc/qdf_nbuf.h b/qdf/inc/qdf_nbuf.h index 54b324bf34..004263194b 100644 --- a/qdf/inc/qdf_nbuf.h +++ b/qdf/inc/qdf_nbuf.h @@ -148,6 +148,9 @@ #define QDF_NBUF_TX_PKT_STATE_MAX 10 #define QDF_NBUF_TX_PKT_LI_DP 11 +/* nbuf allocations only come from one domain */ +#define QDF_DEBUG_NBUF_DOMAIN 0 + /* qdf_nbuf allocate and map max retry threshold when failed */ #define QDF_NBUF_ALLOC_MAP_RETRY_THRESHOLD 20 @@ -408,7 +411,7 @@ struct qdf_radiotap_vendor_ns { } __attribute__((__packed__)); /** - * strcut qdf_radiotap_vendor_ns_ath - Combined QTI Vendor NS + * struct qdf_radiotap_vendor_ns_ath - Combined QTI Vendor NS * including the Radiotap specified Vendor Namespace header and * QTI specific Vendor Namespace data * @lsig: L_SIG_A (or L_SIG) @@ -425,6 +428,8 @@ struct qdf_radiotap_vendor_ns_ath { uint32_t ppdu_start_timestamp; } __attribute__((__packed__)); +#define QDF_MEM_FUNC_NAME_SIZE 48 + /* Masks for HE SIG known fields in mon_rx_status structure */ #define QDF_MON_STATUS_HE_SIG_B_COMMON_KNOWN_RU0 0x00000001 #define QDF_MON_STATUS_HE_SIG_B_COMMON_KNOWN_RU1 0x00000002 @@ -654,6 +659,37 @@ enum cb_ftype { */ typedef __qdf_nbuf_t qdf_nbuf_t; +/** + * struct qdf_nbuf_track_t - Network buffer track structure + * + * @p_next: Pointer to next + * @net_buf: Pointer to network buffer + * @func_name: Function name + * @line_num: Line number + * @size: Size + * @map_func_name: nbuf mapping function name + * @map_line_num: mapping function line number + * @unmap_func_name: nbuf unmapping function name + * @unmap_line_num: mapping function line number + * @is_nbuf_mapped: indicate mapped/unmapped nbuf + * @time: mapping function timestamp + */ +struct qdf_nbuf_track_t { + struct qdf_nbuf_track_t *p_next; + qdf_nbuf_t net_buf; + char func_name[QDF_MEM_FUNC_NAME_SIZE]; + uint32_t line_num; + size_t size; + char map_func_name[QDF_MEM_FUNC_NAME_SIZE]; + uint32_t map_line_num; + char unmap_func_name[QDF_MEM_FUNC_NAME_SIZE]; + uint32_t unmap_line_num; + bool is_nbuf_mapped; + qdf_time_t time; +}; + +typedef struct qdf_nbuf_track_t QDF_NBUF_TRACK; + /** * typedef qdf_nbuf_queue_head_t - Platform indepedent nbuf queue head */ @@ -1454,6 +1490,9 @@ static inline qdf_nbuf_t qdf_nbuf_next(qdf_nbuf_t buf) } #ifdef NBUF_MEMORY_DEBUG + +#define QDF_NET_BUF_TRACK_MAX_SIZE (1024) + void qdf_net_buf_debug_init(void); void qdf_net_buf_debug_exit(void); void qdf_net_buf_debug_clean(void); @@ -3907,6 +3946,39 @@ static inline void qdf_net_buf_debug_release_frag(qdf_nbuf_t buf, } #endif /* NBUF_FRAG_MEMORY_DEBUG */ +#ifdef MEMORY_DEBUG +/** + * qdf_nbuf_acquire_track_lock - acquire the nbuf spinlock at the + * specified index + * @index: index to get the lock + * @irq_flag: lock flag for using nbuf lock + * + * Return: none + */ +void qdf_nbuf_acquire_track_lock(uint32_t index, + unsigned long irq_flag); + +/** + * qdf_nbuf_release_track_lock - release the nbuf spinlock at the + * specified index + * @index: index of the lock to be released + * @irq_flag: lock flag for using nbuf lock + * + * Return: none + */ +void qdf_nbuf_release_track_lock(uint32_t index, + unsigned long irq_flag); + +/** + * qdf_nbuf_get_track_tbl - get the QDF_NBUF_TRACK entry from the track + * table at the specified index + * @index: index to get the table entry + * + * Return: the QDF_NBUF_TRACK entry at the specified index in the table + */ +QDF_NBUF_TRACK *qdf_nbuf_get_track_tbl(uint32_t index); +#endif /* MEMORY_DEBUG */ + #ifdef CONFIG_NBUF_AP_PLATFORM #include #else diff --git a/qdf/linux/src/qdf_mem.c b/qdf/linux/src/qdf_mem.c index d2efd29fe7..dd3385d968 100644 --- a/qdf/linux/src/qdf_mem.c +++ b/qdf/linux/src/qdf_mem.c @@ -73,6 +73,7 @@ static struct __qdf_mem_stat { enum list_type { LIST_TYPE_MEM = 0, LIST_TYPE_DMA = 1, + LIST_TYPE_NBUF = 2, LIST_TYPE_MAX, }; @@ -103,6 +104,13 @@ static struct major_alloc_priv dma_priv = { 50 }; +static struct major_alloc_priv nbuf_priv = { + /* List type set to NBUF */ + LIST_TYPE_NBUF, + /* initial threshold to list APIs which allocates nbuf >= 50 times */ + 50 +}; + static qdf_list_t qdf_mem_domains[QDF_DEBUG_DOMAIN_COUNT]; static qdf_spinlock_t qdf_mem_list_lock; @@ -699,6 +707,181 @@ static ssize_t qdf_major_alloc_set_threshold(struct file *file, return buf_size; } +/** + * qdf_print_major_nbuf_allocs() - output agnostic nbuf print logic + * @threshold: the threshold value set by uset to list top allocations + * @print: the print adapter function + * @print_priv: the private data to be consumed by @print + * @mem_print: pointer to function which prints the memory allocation data + * + * Return: None + */ +static void +qdf_print_major_nbuf_allocs(uint32_t threshold, + qdf_abstract_print print, + void *print_priv, + void (*mem_print)(struct __qdf_mem_info *, + qdf_abstract_print, + void *, uint32_t)) +{ + uint32_t nbuf_iter; + unsigned long irq_flag = 0; + QDF_NBUF_TRACK *p_node; + QDF_NBUF_TRACK *p_prev; + struct __qdf_mem_info table[QDF_MEM_STAT_TABLE_SIZE]; + struct qdf_mem_header meta; + bool is_full; + + qdf_mem_zero(table, sizeof(table)); + qdf_mem_debug_print_header(print, print_priv, threshold); + + if (is_initial_mem_debug_disabled) + return; + + qdf_rl_info("major nbuf print with threshold %u", threshold); + + for (nbuf_iter = 0; nbuf_iter < QDF_NET_BUF_TRACK_MAX_SIZE; + nbuf_iter++) { + qdf_nbuf_acquire_track_lock(nbuf_iter, irq_flag); + p_node = qdf_nbuf_get_track_tbl(nbuf_iter); + while (p_node) { + meta.line = p_node->line_num; + meta.size = p_node->size; + meta.caller = NULL; + meta.time = p_node->time; + qdf_str_lcopy(meta.func, p_node->func_name, + QDF_MEM_FUNC_NAME_SIZE); + + is_full = qdf_mem_meta_table_insert(table, &meta); + + if (is_full) { + (*mem_print)(table, print, + print_priv, threshold); + qdf_mem_zero(table, sizeof(table)); + } + + p_prev = p_node; + p_node = p_node->p_next; + } + qdf_nbuf_release_track_lock(nbuf_iter, irq_flag); + } + + (*mem_print)(table, print, print_priv, threshold); + + qdf_rl_info("major nbuf print end"); +} + +/** + * qdf_major_nbuf_alloc_show() - print sequential callback + * @seq: seq_file handle + * @v: current iterator + * + * Return: 0 - success + */ +static int qdf_major_nbuf_alloc_show(struct seq_file *seq, void *v) +{ + struct major_alloc_priv *priv = (struct major_alloc_priv *)seq->private; + + if (!priv) { + qdf_err("priv is null"); + return -EINVAL; + } + + qdf_print_major_nbuf_allocs(priv->threshold, + seq_printf_printer, + seq, + qdf_print_major_alloc); + + return 0; +} + +/** + * qdf_nbuf_seq_start() - sequential callback to start + * @seq: seq_file handle + * @pos: The start position of the sequence + * + * Return: iterator pointer, or NULL if iteration is complete + */ +static void *qdf_nbuf_seq_start(struct seq_file *seq, loff_t *pos) +{ + enum qdf_debug_domain domain = *pos; + + if (domain > QDF_DEBUG_NBUF_DOMAIN) + return NULL; + + return pos; +} + +/** + * qdf_nbuf_seq_next() - next sequential callback + * @seq: seq_file handle + * @v: the current iterator + * @pos: the current position + * + * Get the next node and release previous node. + * + * Return: iterator pointer, or NULL if iteration is complete + */ +static void *qdf_nbuf_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + + return qdf_nbuf_seq_start(seq, pos); +} + +/** + * qdf_nbuf_seq_stop() - stop sequential callback + * @seq: seq_file handle + * @v: current iterator + * + * Return: None + */ +static void qdf_nbuf_seq_stop(struct seq_file *seq, void *v) { } + +/* sequential file operation table created to track major skb allocs */ +static const struct seq_operations qdf_major_nbuf_allocs_seq_ops = { + .start = qdf_nbuf_seq_start, + .next = qdf_nbuf_seq_next, + .stop = qdf_nbuf_seq_stop, + .show = qdf_major_nbuf_alloc_show, +}; + +static int qdf_major_nbuf_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_nbuf_allocs_seq_ops); + if (rc == 0) { + seq = file->private_data; + seq->private = private; + } + return rc; +} + +static ssize_t qdf_major_nbuf_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, @@ -718,6 +901,16 @@ static const struct file_operations fops_qdf_mem_debugfs = { .release = seq_release, }; +/* file operation table for listing major allocs */ +static const struct file_operations fops_qdf_nbuf_major_allocs = { + .owner = THIS_MODULE, + .open = qdf_major_nbuf_allocs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, + .write = qdf_major_nbuf_alloc_set_threshold, +}; + static QDF_STATUS qdf_mem_debug_debugfs_init(void) { if (is_initial_mem_debug_disabled) @@ -744,6 +937,12 @@ static QDF_STATUS qdf_mem_debug_debugfs_init(void) &dma_priv, &fops_qdf_major_allocs); + debugfs_create_file("major_nbuf_allocs", + 0600, + qdf_mem_debugfs_root, + &nbuf_priv, + &fops_qdf_nbuf_major_allocs); + return QDF_STATUS_SUCCESS; } diff --git a/qdf/linux/src/qdf_nbuf.c b/qdf/linux/src/qdf_nbuf.c index 6ff0f7714e..83c568dc0e 100644 --- a/qdf/linux/src/qdf_nbuf.c +++ b/qdf/linux/src/qdf_nbuf.c @@ -2109,37 +2109,8 @@ bool __qdf_nbuf_is_bcast_pkt(qdf_nbuf_t nbuf) qdf_export_symbol(__qdf_nbuf_is_bcast_pkt); #ifdef NBUF_MEMORY_DEBUG -#define QDF_NET_BUF_TRACK_MAX_SIZE (1024) - -/** - * struct qdf_nbuf_track_t - Network buffer track structure - * - * @p_next: Pointer to next - * @net_buf: Pointer to network buffer - * @func_name: Function name - * @line_num: Line number - * @size: Size - * @map_func_name: nbuf mapping function name - * @map_line_num: mapping function line number - * @unmap_func_name: nbuf unmapping function name - * @unmap_line_num: mapping function line number - * @is_nbuf_mapped: indicate mapped/unmapped nbuf - */ -struct qdf_nbuf_track_t { - struct qdf_nbuf_track_t *p_next; - qdf_nbuf_t net_buf; - char func_name[QDF_MEM_FUNC_NAME_SIZE]; - uint32_t line_num; - size_t size; - char map_func_name[QDF_MEM_FUNC_NAME_SIZE]; - uint32_t map_line_num; - char unmap_func_name[QDF_MEM_FUNC_NAME_SIZE]; - uint32_t unmap_line_num; - bool is_nbuf_mapped; -}; static spinlock_t g_qdf_net_buf_track_lock[QDF_NET_BUF_TRACK_MAX_SIZE]; -typedef struct qdf_nbuf_track_t QDF_NBUF_TRACK; static QDF_NBUF_TRACK *gp_qdf_net_buf_track_tbl[QDF_NET_BUF_TRACK_MAX_SIZE]; static struct kmem_cache *nbuf_tracking_cache; @@ -2551,6 +2522,7 @@ void qdf_net_buf_debug_add_node(qdf_nbuf_t net_buf, size_t size, p_node->map_func_name[0] = '\0'; p_node->unmap_func_name[0] = '\0'; p_node->size = size; + p_node->time = qdf_get_log_timestamp(); qdf_mem_skb_inc(size); p_node->p_next = gp_qdf_net_buf_track_tbl[i]; gp_qdf_net_buf_track_tbl[i] = p_node; @@ -4826,3 +4798,24 @@ void qdf_net_buf_debug_release_frag(qdf_nbuf_t buf, const char *func, qdf_export_symbol(qdf_net_buf_debug_release_frag); #endif /* NBUF_FRAG_MEMORY_DEBUG */ + +#ifdef MEMORY_DEBUG +void qdf_nbuf_acquire_track_lock(uint32_t index, + unsigned long irq_flag) +{ + spin_lock_irqsave(&g_qdf_net_buf_track_lock[index], + irq_flag); +} + +void qdf_nbuf_release_track_lock(uint32_t index, + unsigned long irq_flag) +{ + spin_unlock_irqrestore(&g_qdf_net_buf_track_lock[index], + irq_flag); +} + +QDF_NBUF_TRACK *qdf_nbuf_get_track_tbl(uint32_t index) +{ + return gp_qdf_net_buf_track_tbl[index]; +} +#endif /* MEMORY_DEBUG */