|
@@ -31,6 +31,7 @@
|
|
|
*/
|
|
|
|
|
|
|
|
|
+#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;
|
|
|
+
|
|
|
+
|
|
|
+ for (i = 0; i < QDF_DEBUG_DOMAIN_COUNT; ++i) {
|
|
|
+ qdf_list_t *timers = &qdf_timer_domains[i];
|
|
|
|
|
|
- qdf_mc_timer_node_t *timer_node;
|
|
|
+ if (qdf_list_empty(timers))
|
|
|
+ continue;
|
|
|
|
|
|
- list_size = qdf_list_size(&qdf_timer_list);
|
|
|
+ leaks_detected = true;
|
|
|
|
|
|
- if (!list_size)
|
|
|
+ qdf_err("\nTimer leaks detected in the %s (Id %d) domain!\n",
|
|
|
+ qdf_debug_domain_name(i), i);
|
|
|
+ qdf_mc_timer_print_list(timers);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (!leaks_detected)
|
|
|
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 {
|
|
|
- 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);
|
|
|
- }
|
|
|
- } while (qdf_status == QDF_STATUS_SUCCESS);
|
|
|
+
|
|
|
+ qdf_mc_timer_panic();
|
|
|
+
|
|
|
+
|
|
|
+ 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;
|
|
|
|
|
|
|
|
@@ -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;
|
|
|
|
|
|
|
|
@@ -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) {
|