From c88eb85707d5769b6e907b6192f4e8737fd310d0 Mon Sep 17 00:00:00 2001 From: Bala Venkatesh Date: Sat, 4 May 2019 12:59:12 +0530 Subject: [PATCH] qcacmn: Stop ROC timer synchronously Mc timer is used to initialize the p2p roc timer. And the actual timer runs in the soft irq thread and when the timer exipres it posts the message to mc thread. Currently, qdf_mc_timer_stop is called to stop the timer. It calls the del_timer internally to delete the timer. del_timer() ensures that the given timer is not queued to run anywhere in the system. But the callback may be running on another CPU core can create race conditions. So use del_timer_sync to delete the roc timer. Change-Id: I2c0fd6e335fc342a3acf06ede534c84d40e19346 CRs-Fixed: 2447236 --- qdf/inc/qdf_mc_timer.h | 16 ++++++++++++++ qdf/linux/src/qdf_mc_timer.c | 41 ++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/qdf/inc/qdf_mc_timer.h b/qdf/inc/qdf_mc_timer.h index 19ef611815..5d94df6d98 100644 --- a/qdf/inc/qdf_mc_timer.h +++ b/qdf/inc/qdf_mc_timer.h @@ -226,6 +226,22 @@ QDF_STATUS qdf_mc_timer_start(qdf_mc_timer_t *timer, uint32_t expiration_time); */ QDF_STATUS qdf_mc_timer_stop(qdf_mc_timer_t *timer); +/** + * qdf_mc_timer_stop_sync() - stop a QDF Timer + * @timer: Pointer to timer object + * qdf_mc_timer_stop_sync() function stops a timer synchronously + * that has been started but has not expired, essentially + * cancelling the 'start' request. + * + * After a timer is stopped, it goes back to the state it was in after it + * was created and can be started again via a call to qdf_mc_timer_start(). + * + * Return: + * QDF_STATUS_SUCCESS - Timer is initialized successfully + * QDF failure status - Timer initialization failed + */ +QDF_STATUS qdf_mc_timer_stop_sync(qdf_mc_timer_t *timer); + /** * qdf_mc_timer_get_system_ticks() - get the system time in 10ms ticks * diff --git a/qdf/linux/src/qdf_mc_timer.c b/qdf/linux/src/qdf_mc_timer.c index d69c668f39..d0baa3adbe 100644 --- a/qdf/linux/src/qdf_mc_timer.c +++ b/qdf/linux/src/qdf_mc_timer.c @@ -757,6 +757,47 @@ QDF_STATUS qdf_mc_timer_stop(qdf_mc_timer_t *timer) } qdf_export_symbol(qdf_mc_timer_stop); +QDF_STATUS qdf_mc_timer_stop_sync(qdf_mc_timer_t *timer) +{ + /* check for invalid pointer */ + if (!timer) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + "%s Null timer pointer being passed", __func__); + QDF_ASSERT(0); + return QDF_STATUS_E_INVAL; + } + + /* check if timer refers to an uninitialized object */ + if (LINUX_TIMER_COOKIE != timer->platform_info.cookie) { + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, + "%s: Cannot stop uninitialized timer", __func__); + QDF_ASSERT(0); + + return QDF_STATUS_E_INVAL; + } + + /* ensure the timer state is correct */ + qdf_spin_lock_irqsave(&timer->platform_info.spinlock); + + if (QDF_TIMER_STATE_RUNNING != timer->state) { + qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock); + QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH, + "%s: Cannot stop timer in state = %d", + __func__, timer->state); + return QDF_STATUS_SUCCESS; + } + + timer->state = QDF_TIMER_STATE_STOPPED; + + del_timer_sync(&(timer->platform_info.timer)); + + qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock); + + qdf_try_allowing_sleep(timer->type); + + return QDF_STATUS_SUCCESS; +} +qdf_export_symbol(qdf_mc_timer_stop_sync); /** * qdf_mc_timer_get_system_ticks() - get the system time in 10ms ticks