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:
Dustin Brown
2017-12-06 16:18:53 -08:00
committed by snandini
parent c5ac638d51
commit f7fb76bd5e
2 changed files with 110 additions and 32 deletions

View File

@@ -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

View File

@@ -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) {