qcacmn: Add qdf_mc_timer_check_for_leaks API
Add a new QDF MC Timer API for asserting that there are no active MC Timers allocated. This is useful for detecting MC Timer leaks at runtime. Change-Id: I272ce806111b01b5c7f6b0dfef2e992c27b83529 CRs-Fixed: 2125800
This commit is contained in:
@@ -66,7 +66,7 @@ struct qdf_mc_timer_s;
|
||||
typedef struct qdf_mc_timer_node_s {
|
||||
qdf_list_node_t node;
|
||||
char *file_name;
|
||||
unsigned int line_num;
|
||||
uint32_t line_num;
|
||||
struct qdf_mc_timer_s *qdf_timer;
|
||||
} qdf_mc_timer_node_t;
|
||||
#endif
|
||||
@@ -90,6 +90,7 @@ void qdf_try_allowing_sleep(QDF_TIMER_TYPE type);
|
||||
#ifdef TIMER_MANAGER
|
||||
void qdf_mc_timer_manager_init(void);
|
||||
void qdf_mc_timer_manager_exit(void);
|
||||
void qdf_mc_timer_check_for_leaks(void);
|
||||
#else
|
||||
/**
|
||||
* qdf_mc_timer_manager_init() - initialize QDF debug timer manager
|
||||
@@ -110,6 +111,15 @@ static inline void qdf_mc_timer_manager_init(void)
|
||||
static inline void qdf_mc_timer_manager_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* qdf_mc_timer_check_for_leaks() - Assert there are no active mc timers
|
||||
*
|
||||
* If there are active timers, this API prints them and panics.
|
||||
*
|
||||
* Return: None
|
||||
*/
|
||||
static inline void qdf_mc_timer_check_for_leaks(void) { }
|
||||
#endif
|
||||
/**
|
||||
* qdf_mc_timer_get_current_state() - get the current state of the timer
|
||||
|
@@ -31,6 +31,7 @@
|
||||
*/
|
||||
|
||||
/* Include Files */
|
||||
#include <qdf_debug_domain.h>
|
||||
#include <qdf_mc_timer.h>
|
||||
#include <qdf_lock.h>
|
||||
#include "qdf_lock.h"
|
||||
@@ -146,10 +147,13 @@ EXPORT_SYMBOL(qdf_timer_module_init);
|
||||
|
||||
#ifdef TIMER_MANAGER
|
||||
|
||||
qdf_list_t qdf_timer_list;
|
||||
qdf_spinlock_t qdf_timer_list_lock;
|
||||
static qdf_list_t qdf_timer_domains[QDF_DEBUG_DOMAIN_COUNT];
|
||||
static qdf_spinlock_t qdf_timer_list_lock;
|
||||
|
||||
static void qdf_timer_clean(void);
|
||||
static inline qdf_list_t *qdf_timer_list_get(enum qdf_debug_domain domain)
|
||||
{
|
||||
return &qdf_timer_domains[domain];
|
||||
}
|
||||
|
||||
/**
|
||||
* qdf_mc_timer_manager_init() - initialize QDF debug timer manager
|
||||
@@ -160,11 +164,68 @@ static void qdf_timer_clean(void);
|
||||
*/
|
||||
void qdf_mc_timer_manager_init(void)
|
||||
{
|
||||
qdf_list_create(&qdf_timer_list, 1000);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i)
|
||||
qdf_list_create(&qdf_timer_domains[i], 1000);
|
||||
qdf_spinlock_create(&qdf_timer_list_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(qdf_mc_timer_manager_init);
|
||||
|
||||
static inline void qdf_mc_timer_panic(void)
|
||||
{
|
||||
QDF_BUG(0);
|
||||
}
|
||||
|
||||
static void qdf_mc_timer_print_list(qdf_list_t *timers)
|
||||
{
|
||||
QDF_STATUS status;
|
||||
qdf_list_node_t *node;
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_timer_list_lock);
|
||||
status = qdf_list_peek_front(timers, &node);
|
||||
while (QDF_IS_STATUS_SUCCESS(status)) {
|
||||
qdf_mc_timer_node_t *timer_node = (qdf_mc_timer_node_t *)node;
|
||||
const char *filename = kbasename(timer_node->file_name);
|
||||
uint32_t line = timer_node->line_num;
|
||||
|
||||
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
||||
qdf_err("timer Leak@ File %s, @Line %u", filename, line);
|
||||
qdf_spin_lock_irqsave(&qdf_timer_list_lock);
|
||||
|
||||
status = qdf_list_peek_next(timers, node, &node);
|
||||
}
|
||||
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
||||
}
|
||||
|
||||
void qdf_mc_timer_check_for_leaks(void)
|
||||
{
|
||||
enum qdf_debug_domain current_domain = qdf_debug_domain_get();
|
||||
qdf_list_t *timers = qdf_timer_list_get(current_domain);
|
||||
|
||||
if (qdf_list_empty(timers))
|
||||
return;
|
||||
|
||||
qdf_err("Timer leaks detected in %s domain!",
|
||||
qdf_debug_domain_name(current_domain));
|
||||
qdf_mc_timer_print_list(timers);
|
||||
qdf_mc_timer_panic();
|
||||
}
|
||||
|
||||
static void qdf_mc_timer_free_leaked_timers(qdf_list_t *timers)
|
||||
{
|
||||
QDF_STATUS status;
|
||||
qdf_list_node_t *node;
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_timer_list_lock);
|
||||
status = qdf_list_remove_front(timers, &node);
|
||||
while (QDF_IS_STATUS_SUCCESS(status)) {
|
||||
qdf_mem_free(node);
|
||||
status = qdf_list_remove_front(timers, &node);
|
||||
}
|
||||
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* qdf_timer_clean() - clean up QDF timer debug functionality
|
||||
*
|
||||
@@ -175,33 +236,33 @@ EXPORT_SYMBOL(qdf_mc_timer_manager_init);
|
||||
*/
|
||||
static void qdf_timer_clean(void)
|
||||
{
|
||||
uint32_t list_size;
|
||||
qdf_list_node_t *node;
|
||||
QDF_STATUS qdf_status;
|
||||
bool leaks_detected = false;
|
||||
int i;
|
||||
|
||||
qdf_mc_timer_node_t *timer_node;
|
||||
/* detect and print leaks */
|
||||
for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i) {
|
||||
qdf_list_t *timers = &qdf_timer_domains[i];
|
||||
|
||||
list_size = qdf_list_size(&qdf_timer_list);
|
||||
if (qdf_list_empty(timers))
|
||||
continue;
|
||||
|
||||
if (!list_size)
|
||||
return;
|
||||
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH,
|
||||
"%s: List is not Empty. list_size %d ",
|
||||
__func__, (int)list_size);
|
||||
leaks_detected = true;
|
||||
|
||||
do {
|
||||
qdf_spin_lock_irqsave(&qdf_timer_list_lock);
|
||||
qdf_status = qdf_list_remove_front(&qdf_timer_list, &node);
|
||||
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
||||
if (QDF_STATUS_SUCCESS == qdf_status) {
|
||||
timer_node = (qdf_mc_timer_node_t *) node;
|
||||
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_FATAL,
|
||||
"timer Leak@ File %s, @Line %d",
|
||||
timer_node->file_name,
|
||||
(int)timer_node->line_num);
|
||||
qdf_mem_free(timer_node);
|
||||
qdf_err("\nTimer leaks detected in the %s (Id %d) domain!\n",
|
||||
qdf_debug_domain_name(i), i);
|
||||
qdf_mc_timer_print_list(timers);
|
||||
}
|
||||
} while (qdf_status == QDF_STATUS_SUCCESS);
|
||||
|
||||
/* we're done if there were no leaks */
|
||||
if (!leaks_detected)
|
||||
return;
|
||||
|
||||
/* panic, if enabled */
|
||||
qdf_mc_timer_panic();
|
||||
|
||||
/* if we didn't crash, release the leaked timers */
|
||||
for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i)
|
||||
qdf_mc_timer_free_leaked_timers(&qdf_timer_domains[i]);
|
||||
}
|
||||
EXPORT_SYMBOL(qdf_timer_clean);
|
||||
|
||||
@@ -214,8 +275,13 @@ EXPORT_SYMBOL(qdf_timer_clean);
|
||||
*/
|
||||
void qdf_mc_timer_manager_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
qdf_timer_clean();
|
||||
qdf_list_destroy(&qdf_timer_list);
|
||||
|
||||
for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i)
|
||||
qdf_list_destroy(&qdf_timer_domains[i]);
|
||||
|
||||
qdf_spinlock_destroy(&qdf_timer_list_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(qdf_mc_timer_manager_exit);
|
||||
@@ -259,6 +325,8 @@ QDF_STATUS qdf_mc_timer_init_debug(qdf_mc_timer_t *timer,
|
||||
void *user_data, char *file_name,
|
||||
uint32_t line_num)
|
||||
{
|
||||
enum qdf_debug_domain current_domain = qdf_debug_domain_get();
|
||||
qdf_list_t *active_timers = qdf_timer_list_get(current_domain);
|
||||
QDF_STATUS qdf_status;
|
||||
|
||||
/* check for invalid pointer */
|
||||
@@ -279,14 +347,12 @@ QDF_STATUS qdf_mc_timer_init_debug(qdf_mc_timer_t *timer,
|
||||
return QDF_STATUS_E_NOMEM;
|
||||
}
|
||||
|
||||
qdf_mem_set(timer->timer_node, sizeof(qdf_mc_timer_node_t), 0);
|
||||
|
||||
timer->timer_node->file_name = file_name;
|
||||
timer->timer_node->line_num = line_num;
|
||||
timer->timer_node->qdf_timer = timer;
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_timer_list_lock);
|
||||
qdf_status = qdf_list_insert_front(&qdf_timer_list,
|
||||
qdf_status = qdf_list_insert_front(active_timers,
|
||||
&timer->timer_node->node);
|
||||
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
||||
if (QDF_STATUS_SUCCESS != qdf_status) {
|
||||
@@ -374,6 +440,8 @@ EXPORT_SYMBOL(qdf_mc_timer_init);
|
||||
#ifdef TIMER_MANAGER
|
||||
QDF_STATUS qdf_mc_timer_destroy(qdf_mc_timer_t *timer)
|
||||
{
|
||||
enum qdf_debug_domain current_domain = qdf_debug_domain_get();
|
||||
qdf_list_t *active_timers = qdf_timer_list_get(current_domain);
|
||||
QDF_STATUS v_status = QDF_STATUS_SUCCESS;
|
||||
|
||||
/* check for invalid pointer */
|
||||
@@ -393,7 +461,7 @@ QDF_STATUS qdf_mc_timer_destroy(qdf_mc_timer_t *timer)
|
||||
}
|
||||
|
||||
qdf_spin_lock_irqsave(&qdf_timer_list_lock);
|
||||
v_status = qdf_list_remove_node(&qdf_timer_list,
|
||||
v_status = qdf_list_remove_node(active_timers,
|
||||
&timer->timer_node->node);
|
||||
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
||||
if (v_status != QDF_STATUS_SUCCESS) {
|
||||
|
Reference in New Issue
Block a user