From 778ebe552a2ef1dd8feb519903e21f549d549a15 Mon Sep 17 00:00:00 2001 From: Dustin Brown Date: Wed, 13 Mar 2019 11:46:58 -0700 Subject: [PATCH] qcacmn: Add periodic work tracking Add debug tracking to qdf_periodic_work for create and destroy calls. Change-Id: Id595ba05493ba196e497533ffb4f4e86e33df4b8 CRs-Fixed: 2423644 --- qdf/inc/qdf_periodic_work.h | 43 +++++++++++++++++++-- qdf/linux/src/qdf_periodic_work.c | 63 +++++++++++++++++++++++++++++-- 2 files changed, 98 insertions(+), 8 deletions(-) diff --git a/qdf/inc/qdf_periodic_work.h b/qdf/inc/qdf_periodic_work.h index 905e13d03e15..56d1dd8da66b 100644 --- a/qdf/inc/qdf_periodic_work.h +++ b/qdf/inc/qdf_periodic_work.h @@ -54,17 +54,25 @@ struct qdf_periodic_work { * * Return: QDF_STATUS */ +#define qdf_periodic_work_create(pwork, callback, context) \ + __qdf_periodic_work_create(pwork, callback, context, __func__, __LINE__) + qdf_must_check QDF_STATUS -qdf_periodic_work_create(struct qdf_periodic_work *pwork, - qdf_periodic_work_cb callback, void *context); +__qdf_periodic_work_create(struct qdf_periodic_work *pwork, + qdf_periodic_work_cb callback, void *context, + const char *func, uint32_t line); /** * qdf_periodic_work_destroy() - deinitialize a periodic work @pwork - * @pwork: the periodic work to initialize + * @pwork: the periodic work to de-initialize * * Return: None */ -void qdf_periodic_work_destroy(struct qdf_periodic_work *pwork); +#define qdf_periodic_work_destroy(pwork) \ + __qdf_periodic_work_destroy(pwork, __func__, __LINE__) + +void __qdf_periodic_work_destroy(struct qdf_periodic_work *pwork, + const char *func, uint32_t line); /** * qdf_periodic_work_start() - begin periodic execution of @pwork callback @@ -101,5 +109,32 @@ bool qdf_periodic_work_stop_async(struct qdf_periodic_work *pwork); */ bool qdf_periodic_work_stop_sync(struct qdf_periodic_work *pwork); +#ifdef WLAN_PERIODIC_WORK_DEBUG +/** + * qdf_periodic_work_check_for_leaks() - assert no periodic work leaks + * + * Return: None + */ +void qdf_periodic_work_check_for_leaks(void); + +/** + * qdf_periodic_work_feature_init() - global init logic for periodic work + * + * Return: None + */ +void qdf_periodic_work_feature_init(void); + +/** + * qdf_periodic_work_feature_deinit() - global de-init logic for periodic work + * + * Return: None + */ +void qdf_periodic_work_feature_deinit(void); +#else +static inline void qdf_periodic_work_check_for_leaks(void) { } +static inline void qdf_periodic_work_feature_init(void) { } +static inline void qdf_periodic_work_feature_deinit(void) { } +#endif /* WLAN_PERIODIC_WORK_DEBUG */ + #endif /* __QDF_PERIODIC_WORK_H */ diff --git a/qdf/linux/src/qdf_periodic_work.c b/qdf/linux/src/qdf_periodic_work.c index 61e1c59f2730..15aeb3435e1c 100644 --- a/qdf/linux/src/qdf_periodic_work.c +++ b/qdf/linux/src/qdf_periodic_work.c @@ -21,6 +21,52 @@ #include "qdf_trace.h" #include "qdf_types.h" +#ifdef WLAN_PERIODIC_WORK_DEBUG +#include "qdf_tracker.h" + +#define qdf_pwork_tracker_bits 2 /* 4 buckets */ +static qdf_tracker_declare(qdf_pwork_tracker, qdf_pwork_tracker_bits, + "periodic work leaks", "periodic work create", + "periodic work destroy"); + +void qdf_periodic_work_feature_init(void) +{ + qdf_tracker_init(&qdf_pwork_tracker); +} + +void qdf_periodic_work_feature_deinit(void) +{ + qdf_tracker_deinit(&qdf_pwork_tracker); +} + +void qdf_periodic_work_check_for_leaks(void) +{ + qdf_tracker_check_for_leaks(&qdf_pwork_tracker); +} + +static inline QDF_STATUS qdf_pwork_dbg_track(struct qdf_periodic_work *pwork, + const char *func, uint32_t line) +{ + return qdf_tracker_track(&qdf_pwork_tracker, pwork, func, line); +} + +static inline void qdf_pwork_dbg_untrack(struct qdf_periodic_work *pwork, + const char *func, uint32_t line) +{ + qdf_tracker_untrack(&qdf_pwork_tracker, pwork, func, line); +} +#else +static inline QDF_STATUS qdf_pwork_dbg_track(struct qdf_periodic_work *pwork, + const char *func, uint32_t line) +{ + return QDF_STATUS_SUCCESS; +} + +static inline void qdf_pwork_dbg_untrack(struct qdf_periodic_work *pwork, + const char *func, uint32_t line) +{ } +#endif /* WLAN_PERIODIC_WORK_DEBUG */ + static void __qdf_periodic_work_handler(struct work_struct *work) { struct qdf_periodic_work *pwork = @@ -35,15 +81,22 @@ static void __qdf_periodic_work_handler(struct work_struct *work) schedule_delayed_work(&pwork->dwork, msecs_to_jiffies(msec)); } -QDF_STATUS qdf_periodic_work_create(struct qdf_periodic_work *pwork, - qdf_periodic_work_cb callback, - void *context) +QDF_STATUS __qdf_periodic_work_create(struct qdf_periodic_work *pwork, + qdf_periodic_work_cb callback, + void *context, + const char *func, uint32_t line) { + QDF_STATUS status; + QDF_BUG(pwork); QDF_BUG(callback); if (!pwork || !callback) return QDF_STATUS_E_INVAL; + status = qdf_pwork_dbg_track(pwork, func, line); + if (QDF_IS_STATUS_ERROR(status)) + return status; + INIT_DELAYED_WORK(&pwork->dwork, __qdf_periodic_work_handler); pwork->callback = callback; pwork->context = context; @@ -52,9 +105,11 @@ QDF_STATUS qdf_periodic_work_create(struct qdf_periodic_work *pwork, return QDF_STATUS_SUCCESS; } -void qdf_periodic_work_destroy(struct qdf_periodic_work *pwork) +void __qdf_periodic_work_destroy(struct qdf_periodic_work *pwork, + const char *func, uint32_t line) { qdf_periodic_work_stop_sync(pwork); + qdf_pwork_dbg_untrack(pwork, func, line); } bool qdf_periodic_work_start(struct qdf_periodic_work *pwork, uint32_t msec)