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 {
|
typedef struct qdf_mc_timer_node_s {
|
||||||
qdf_list_node_t node;
|
qdf_list_node_t node;
|
||||||
char *file_name;
|
char *file_name;
|
||||||
unsigned int line_num;
|
uint32_t line_num;
|
||||||
struct qdf_mc_timer_s *qdf_timer;
|
struct qdf_mc_timer_s *qdf_timer;
|
||||||
} qdf_mc_timer_node_t;
|
} qdf_mc_timer_node_t;
|
||||||
#endif
|
#endif
|
||||||
@@ -90,6 +90,7 @@ void qdf_try_allowing_sleep(QDF_TIMER_TYPE type);
|
|||||||
#ifdef TIMER_MANAGER
|
#ifdef TIMER_MANAGER
|
||||||
void qdf_mc_timer_manager_init(void);
|
void qdf_mc_timer_manager_init(void);
|
||||||
void qdf_mc_timer_manager_exit(void);
|
void qdf_mc_timer_manager_exit(void);
|
||||||
|
void qdf_mc_timer_check_for_leaks(void);
|
||||||
#else
|
#else
|
||||||
/**
|
/**
|
||||||
* qdf_mc_timer_manager_init() - initialize QDF debug timer manager
|
* 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)
|
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
|
#endif
|
||||||
/**
|
/**
|
||||||
* qdf_mc_timer_get_current_state() - get the current state of the timer
|
* qdf_mc_timer_get_current_state() - get the current state of the timer
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* Include Files */
|
/* Include Files */
|
||||||
|
#include <qdf_debug_domain.h>
|
||||||
#include <qdf_mc_timer.h>
|
#include <qdf_mc_timer.h>
|
||||||
#include <qdf_lock.h>
|
#include <qdf_lock.h>
|
||||||
#include "qdf_lock.h"
|
#include "qdf_lock.h"
|
||||||
@@ -146,10 +147,13 @@ EXPORT_SYMBOL(qdf_timer_module_init);
|
|||||||
|
|
||||||
#ifdef TIMER_MANAGER
|
#ifdef TIMER_MANAGER
|
||||||
|
|
||||||
qdf_list_t qdf_timer_list;
|
static qdf_list_t qdf_timer_domains[QDF_DEBUG_DOMAIN_COUNT];
|
||||||
qdf_spinlock_t qdf_timer_list_lock;
|
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
|
* 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)
|
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);
|
qdf_spinlock_create(&qdf_timer_list_lock);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qdf_mc_timer_manager_init);
|
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
|
* 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)
|
static void qdf_timer_clean(void)
|
||||||
{
|
{
|
||||||
uint32_t list_size;
|
bool leaks_detected = false;
|
||||||
qdf_list_node_t *node;
|
int i;
|
||||||
QDF_STATUS qdf_status;
|
|
||||||
|
|
||||||
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)
|
leaks_detected = true;
|
||||||
|
|
||||||
|
qdf_err("\nTimer leaks detected in the %s (Id %d) domain!\n",
|
||||||
|
qdf_debug_domain_name(i), i);
|
||||||
|
qdf_mc_timer_print_list(timers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we're done if there were no leaks */
|
||||||
|
if (!leaks_detected)
|
||||||
return;
|
return;
|
||||||
QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH,
|
|
||||||
"%s: List is not Empty. list_size %d ",
|
|
||||||
__func__, (int)list_size);
|
|
||||||
|
|
||||||
do {
|
/* panic, if enabled */
|
||||||
qdf_spin_lock_irqsave(&qdf_timer_list_lock);
|
qdf_mc_timer_panic();
|
||||||
qdf_status = qdf_list_remove_front(&qdf_timer_list, &node);
|
|
||||||
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
/* if we didn't crash, release the leaked timers */
|
||||||
if (QDF_STATUS_SUCCESS == qdf_status) {
|
for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i)
|
||||||
timer_node = (qdf_mc_timer_node_t *) node;
|
qdf_mc_timer_free_leaked_timers(&qdf_timer_domains[i]);
|
||||||
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);
|
|
||||||
}
|
|
||||||
} while (qdf_status == QDF_STATUS_SUCCESS);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qdf_timer_clean);
|
EXPORT_SYMBOL(qdf_timer_clean);
|
||||||
|
|
||||||
@@ -214,8 +275,13 @@ EXPORT_SYMBOL(qdf_timer_clean);
|
|||||||
*/
|
*/
|
||||||
void qdf_mc_timer_manager_exit(void)
|
void qdf_mc_timer_manager_exit(void)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
qdf_timer_clean();
|
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);
|
qdf_spinlock_destroy(&qdf_timer_list_lock);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(qdf_mc_timer_manager_exit);
|
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,
|
void *user_data, char *file_name,
|
||||||
uint32_t line_num)
|
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;
|
QDF_STATUS qdf_status;
|
||||||
|
|
||||||
/* check for invalid pointer */
|
/* 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;
|
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->file_name = file_name;
|
||||||
timer->timer_node->line_num = line_num;
|
timer->timer_node->line_num = line_num;
|
||||||
timer->timer_node->qdf_timer = timer;
|
timer->timer_node->qdf_timer = timer;
|
||||||
|
|
||||||
qdf_spin_lock_irqsave(&qdf_timer_list_lock);
|
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);
|
&timer->timer_node->node);
|
||||||
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
||||||
if (QDF_STATUS_SUCCESS != qdf_status) {
|
if (QDF_STATUS_SUCCESS != qdf_status) {
|
||||||
@@ -374,6 +440,8 @@ EXPORT_SYMBOL(qdf_mc_timer_init);
|
|||||||
#ifdef TIMER_MANAGER
|
#ifdef TIMER_MANAGER
|
||||||
QDF_STATUS qdf_mc_timer_destroy(qdf_mc_timer_t *timer)
|
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;
|
QDF_STATUS v_status = QDF_STATUS_SUCCESS;
|
||||||
|
|
||||||
/* check for invalid pointer */
|
/* 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);
|
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);
|
&timer->timer_node->node);
|
||||||
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
qdf_spin_unlock_irqrestore(&qdf_timer_list_lock);
|
||||||
if (v_status != QDF_STATUS_SUCCESS) {
|
if (v_status != QDF_STATUS_SUCCESS) {
|
||||||
|
Reference in New Issue
Block a user