qcacld-3.0: Add transition timeout detection to DSC
In order to catch and debug long running transitions, add a watchdog timer to Driver Synchronization Core (DSC) transition start/stop call pairs. If the timer expires, panic the driver for offline debugging. Change-Id: I9b64fdb9cc20e1225394702d58b24db92a2d67e1 CRs-Fixed: 2328596
This commit is contained in:
@@ -171,6 +171,47 @@ bool __dsc_ops_remove(struct dsc_ops *ops, const char *func)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WLAN_DSC_DEBUG
|
#ifdef WLAN_DSC_DEBUG
|
||||||
|
static void __dsc_dbg_trans_timeout(void *opaque_trans)
|
||||||
|
{
|
||||||
|
struct dsc_trans *trans = opaque_trans;
|
||||||
|
|
||||||
|
QDF_DEBUG_PANIC("Transition '%s' exceeded %ums",
|
||||||
|
trans->active_desc, DSC_TRANS_TIMEOUT_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __dsc_dbg_trans_timeout_start() - start a timeout timer for @trans
|
||||||
|
* @trans: the active transition to start a timeout timer for
|
||||||
|
*
|
||||||
|
* Return: QDF_STATUS
|
||||||
|
*/
|
||||||
|
static QDF_STATUS __dsc_dbg_trans_timeout_start(struct dsc_trans *trans)
|
||||||
|
{
|
||||||
|
QDF_STATUS status;
|
||||||
|
|
||||||
|
status = qdf_timer_init(NULL, &trans->timeout_timer,
|
||||||
|
__dsc_dbg_trans_timeout, trans,
|
||||||
|
QDF_TIMER_TYPE_SW);
|
||||||
|
if (QDF_IS_STATUS_ERROR(status))
|
||||||
|
return status;
|
||||||
|
|
||||||
|
qdf_timer_start(&trans->timeout_timer, DSC_TRANS_TIMEOUT_MS);
|
||||||
|
|
||||||
|
return QDF_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __dsc_dbg_trans_timeout_stop() - stop the timeout timer for @trans
|
||||||
|
* @trans: the active transition to stop the timeout timer for
|
||||||
|
*
|
||||||
|
* Return: None
|
||||||
|
*/
|
||||||
|
static void __dsc_dbg_trans_timeout_stop(struct dsc_trans *trans)
|
||||||
|
{
|
||||||
|
qdf_timer_stop(&trans->timeout_timer);
|
||||||
|
qdf_timer_free(&trans->timeout_timer);
|
||||||
|
}
|
||||||
|
|
||||||
static void __dsc_dbg_tran_wait_timeout(void *opaque_tran)
|
static void __dsc_dbg_tran_wait_timeout(void *opaque_tran)
|
||||||
{
|
{
|
||||||
struct dsc_tran *tran = opaque_tran;
|
struct dsc_tran *tran = opaque_tran;
|
||||||
@@ -181,7 +222,7 @@ static void __dsc_dbg_tran_wait_timeout(void *opaque_tran)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* __dsc_dbg_tran_wait_timeout_start() - start a timeout timer for @tran
|
* __dsc_dbg_tran_wait_timeout_start() - start a timeout timer for @tran
|
||||||
* @tran: the transition to start a timeout timer for
|
* @tran: the pending transition to start a timeout timer for
|
||||||
*
|
*
|
||||||
* Return: QDF_STATUS
|
* Return: QDF_STATUS
|
||||||
*/
|
*/
|
||||||
@@ -202,7 +243,7 @@ static QDF_STATUS __dsc_dbg_tran_wait_timeout_start(struct dsc_tran *tran)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* __dsc_dbg_tran_wait_timeout_stop() - stop the timeout timer for @tran
|
* __dsc_dbg_tran_wait_timeout_stop() - stop the timeout timer for @tran
|
||||||
* @tran: the transition to stop the timeout timer for
|
* @tran: the pending transition to stop the timeout timer for
|
||||||
*
|
*
|
||||||
* Return: None
|
* Return: None
|
||||||
*/
|
*/
|
||||||
@@ -212,6 +253,13 @@ static void __dsc_dbg_tran_wait_timeout_stop(struct dsc_tran *tran)
|
|||||||
qdf_timer_free(&tran->timeout_timer);
|
qdf_timer_free(&tran->timeout_timer);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
static inline QDF_STATUS __dsc_dbg_trans_timeout_start(struct dsc_trans *trans)
|
||||||
|
{
|
||||||
|
return QDF_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __dsc_dbg_trans_timeout_stop(struct dsc_trans *trans) { }
|
||||||
|
|
||||||
static inline QDF_STATUS
|
static inline QDF_STATUS
|
||||||
__dsc_dbg_tran_wait_timeout_start(struct dsc_tran *tran)
|
__dsc_dbg_tran_wait_timeout_start(struct dsc_tran *tran)
|
||||||
{
|
{
|
||||||
@@ -233,6 +281,27 @@ void __dsc_trans_deinit(struct dsc_trans *trans)
|
|||||||
trans->active_desc = NULL;
|
trans->active_desc = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QDF_STATUS __dsc_trans_start(struct dsc_trans *trans, const char *desc)
|
||||||
|
{
|
||||||
|
QDF_STATUS status;
|
||||||
|
|
||||||
|
status = __dsc_dbg_trans_timeout_start(trans);
|
||||||
|
if (QDF_IS_STATUS_ERROR(status))
|
||||||
|
return status;
|
||||||
|
|
||||||
|
dsc_assert(!trans->active_desc);
|
||||||
|
trans->active_desc = desc;
|
||||||
|
|
||||||
|
return QDF_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __dsc_trans_stop(struct dsc_trans *trans)
|
||||||
|
{
|
||||||
|
dsc_assert(trans->active_desc);
|
||||||
|
trans->active_desc = NULL;
|
||||||
|
__dsc_dbg_trans_timeout_stop(trans);
|
||||||
|
}
|
||||||
|
|
||||||
QDF_STATUS __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran,
|
QDF_STATUS __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran,
|
||||||
const char *desc)
|
const char *desc)
|
||||||
{
|
{
|
||||||
@@ -300,7 +369,7 @@ bool __dsc_trans_trigger(struct dsc_trans *trans)
|
|||||||
if (!tran)
|
if (!tran)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
trans->active_desc = tran->desc;
|
__dsc_trans_start(trans, tran->desc);
|
||||||
qdf_event_set(&tran->event);
|
qdf_event_set(&tran->event);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@@ -56,6 +56,7 @@ static inline bool __dsc_assert(const bool cond, const char *cond_str,
|
|||||||
|
|
||||||
#ifdef WLAN_DSC_DEBUG
|
#ifdef WLAN_DSC_DEBUG
|
||||||
#define DSC_OP_TIMEOUT_MS (1 * 60 * 1000) /* 1 minute */
|
#define DSC_OP_TIMEOUT_MS (1 * 60 * 1000) /* 1 minute */
|
||||||
|
#define DSC_TRANS_TIMEOUT_MS (1 * 60 * 1000) /* 1 minute */
|
||||||
#define DSC_TRANS_WAIT_TIMEOUT_MS (2 * 60 * 1000) /* 2 minutes */
|
#define DSC_TRANS_WAIT_TIMEOUT_MS (2 * 60 * 1000) /* 2 minutes */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -107,10 +108,14 @@ struct dsc_tran {
|
|||||||
* struct dsc_trans - transition information container
|
* struct dsc_trans - transition information container
|
||||||
* @active_desc: unique description of the current transition in progress
|
* @active_desc: unique description of the current transition in progress
|
||||||
* @queue: queue of pending transitions
|
* @queue: queue of pending transitions
|
||||||
|
* @timeout_timer: a timer used to detect transition timeouts
|
||||||
*/
|
*/
|
||||||
struct dsc_trans {
|
struct dsc_trans {
|
||||||
const char *active_desc;
|
const char *active_desc;
|
||||||
qdf_list_t queue;
|
qdf_list_t queue;
|
||||||
|
#ifdef WLAN_DSC_DEBUG
|
||||||
|
qdf_timer_t timeout_timer;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -229,6 +234,23 @@ void __dsc_trans_init(struct dsc_trans *trans);
|
|||||||
*/
|
*/
|
||||||
void __dsc_trans_deinit(struct dsc_trans *trans);
|
void __dsc_trans_deinit(struct dsc_trans *trans);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __dsc_trans_start() - set the active transition on @trans
|
||||||
|
* @trans: the transition container used to track the new active transition
|
||||||
|
* @desc: unique description of the transition being started
|
||||||
|
*
|
||||||
|
* Return: QDF_STATUS
|
||||||
|
*/
|
||||||
|
QDF_STATUS __dsc_trans_start(struct dsc_trans *trans, const char *desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __dsc_trans_stop() - unset the active transition on @trans
|
||||||
|
* @trans: the transition container currently tracking the active transition
|
||||||
|
*
|
||||||
|
* Return: None
|
||||||
|
*/
|
||||||
|
void __dsc_trans_stop(struct dsc_trans *trans);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __dsc_trans_queue() - queue @tran at the back of @trans
|
* __dsc_trans_queue() - queue @tran at the back of @trans
|
||||||
* @trans: the transitions container to enqueue to
|
* @trans: the transitions container to enqueue to
|
||||||
|
@@ -145,9 +145,7 @@ __dsc_driver_trans_start_nolock(struct dsc_driver *driver, const char *desc)
|
|||||||
if (!__dsc_driver_can_trans(driver))
|
if (!__dsc_driver_can_trans(driver))
|
||||||
return QDF_STATUS_E_AGAIN;
|
return QDF_STATUS_E_AGAIN;
|
||||||
|
|
||||||
driver->trans.active_desc = desc;
|
return __dsc_trans_start(&driver->trans, desc);
|
||||||
|
|
||||||
return QDF_STATUS_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QDF_STATUS
|
static QDF_STATUS
|
||||||
@@ -260,8 +258,7 @@ static void __dsc_driver_trans_stop(struct dsc_driver *driver)
|
|||||||
|
|
||||||
__dsc_lock(driver);
|
__dsc_lock(driver);
|
||||||
|
|
||||||
dsc_assert(driver->trans.active_desc);
|
__dsc_trans_stop(&driver->trans);
|
||||||
driver->trans.active_desc = NULL;
|
|
||||||
__dsc_driver_trigger_trans(driver);
|
__dsc_driver_trigger_trans(driver);
|
||||||
|
|
||||||
__dsc_unlock(driver);
|
__dsc_unlock(driver);
|
||||||
@@ -280,7 +277,7 @@ static void __dsc_driver_trans_assert(struct dsc_driver *driver)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
__dsc_lock(driver);
|
__dsc_lock(driver);
|
||||||
dsc_assert(driver->trans.active_desc);
|
dsc_assert(__dsc_trans_active(&driver->trans));
|
||||||
__dsc_unlock(driver);
|
__dsc_unlock(driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -150,9 +150,7 @@ __dsc_psoc_trans_start_nolock(struct dsc_psoc *psoc, const char *desc)
|
|||||||
if (!__dsc_psoc_can_trans(psoc))
|
if (!__dsc_psoc_can_trans(psoc))
|
||||||
return QDF_STATUS_E_AGAIN;
|
return QDF_STATUS_E_AGAIN;
|
||||||
|
|
||||||
psoc->trans.active_desc = desc;
|
return __dsc_trans_start(&psoc->trans, desc);
|
||||||
|
|
||||||
return QDF_STATUS_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QDF_STATUS
|
static QDF_STATUS
|
||||||
@@ -249,8 +247,7 @@ static void __dsc_psoc_trans_stop(struct dsc_psoc *psoc)
|
|||||||
|
|
||||||
__dsc_driver_lock(psoc);
|
__dsc_driver_lock(psoc);
|
||||||
|
|
||||||
dsc_assert(psoc->trans.active_desc);
|
__dsc_trans_stop(&psoc->trans);
|
||||||
psoc->trans.active_desc = NULL;
|
|
||||||
__dsc_psoc_trigger_trans(psoc);
|
__dsc_psoc_trigger_trans(psoc);
|
||||||
|
|
||||||
__dsc_driver_unlock(psoc);
|
__dsc_driver_unlock(psoc);
|
||||||
@@ -269,7 +266,7 @@ static void __dsc_psoc_trans_assert(struct dsc_psoc *psoc)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
__dsc_driver_lock(psoc);
|
__dsc_driver_lock(psoc);
|
||||||
dsc_assert(psoc->trans.active_desc);
|
dsc_assert(__dsc_trans_active(&psoc->trans));
|
||||||
__dsc_driver_unlock(psoc);
|
__dsc_driver_unlock(psoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -124,9 +124,7 @@ __dsc_vdev_trans_start_nolock(struct dsc_vdev *vdev, const char *desc)
|
|||||||
if (!__dsc_vdev_can_trans(vdev))
|
if (!__dsc_vdev_can_trans(vdev))
|
||||||
return QDF_STATUS_E_AGAIN;
|
return QDF_STATUS_E_AGAIN;
|
||||||
|
|
||||||
vdev->trans.active_desc = desc;
|
return __dsc_trans_start(&vdev->trans, desc);
|
||||||
|
|
||||||
return QDF_STATUS_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QDF_STATUS
|
static QDF_STATUS
|
||||||
@@ -220,8 +218,7 @@ static void __dsc_vdev_trans_stop(struct dsc_vdev *vdev)
|
|||||||
|
|
||||||
__dsc_driver_lock(vdev);
|
__dsc_driver_lock(vdev);
|
||||||
|
|
||||||
dsc_assert(vdev->trans.active_desc);
|
__dsc_trans_stop(&vdev->trans);
|
||||||
vdev->trans.active_desc = NULL;
|
|
||||||
__dsc_vdev_trigger_trans(vdev);
|
__dsc_vdev_trigger_trans(vdev);
|
||||||
|
|
||||||
__dsc_driver_unlock(vdev);
|
__dsc_driver_unlock(vdev);
|
||||||
@@ -240,7 +237,7 @@ static void __dsc_vdev_trans_assert(struct dsc_vdev *vdev)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
__dsc_driver_lock(vdev);
|
__dsc_driver_lock(vdev);
|
||||||
dsc_assert(vdev->trans.active_desc);
|
dsc_assert(__dsc_trans_active(&vdev->trans));
|
||||||
__dsc_driver_unlock(vdev);
|
__dsc_driver_unlock(vdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,14 +24,16 @@
|
|||||||
#include "wlan_dsc.h"
|
#include "wlan_dsc.h"
|
||||||
#include "wlan_dsc_test.h"
|
#include "wlan_dsc_test.h"
|
||||||
|
|
||||||
#define dsc_driver_trans_start(driver) dsc_driver_trans_start(driver, "")
|
#define dsc_driver_trans_start(driver) dsc_driver_trans_start(driver, __func__)
|
||||||
#define dsc_psoc_trans_start(psoc) dsc_psoc_trans_start(psoc, "")
|
#define dsc_psoc_trans_start(psoc) dsc_psoc_trans_start(psoc, __func__)
|
||||||
#define dsc_vdev_trans_start(vdev) dsc_vdev_trans_start(vdev, "")
|
#define dsc_vdev_trans_start(vdev) dsc_vdev_trans_start(vdev, __func__)
|
||||||
|
|
||||||
#define dsc_driver_trans_start_wait(driver) \
|
#define dsc_driver_trans_start_wait(driver) \
|
||||||
dsc_driver_trans_start_wait(driver, "")
|
dsc_driver_trans_start_wait(driver, "")
|
||||||
#define dsc_psoc_trans_start_wait(psoc) dsc_psoc_trans_start_wait(psoc, "")
|
#define dsc_psoc_trans_start_wait(psoc) \
|
||||||
#define dsc_vdev_trans_start_wait(vdev) dsc_vdev_trans_start_wait(vdev, "")
|
dsc_psoc_trans_start_wait(psoc, __func__)
|
||||||
|
#define dsc_vdev_trans_start_wait(vdev) \
|
||||||
|
dsc_vdev_trans_start_wait(vdev, __func__)
|
||||||
|
|
||||||
static struct dsc_psoc *nth_psoc(struct dsc_driver *driver, int n)
|
static struct dsc_psoc *nth_psoc(struct dsc_driver *driver, int n)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user