summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMahesh A Saptasagar <c_msapta@qti.qualcomm.com>2016-06-08 13:05:41 +0530
committerAnjaneedevi Kapparapu <akappa@codeaurora.org>2016-08-22 17:27:29 +0530
commitd2582e7bcf2ecad865c540eed5b28749dcedd3c8 (patch)
treefd7e08a2d31a8eedee97788810d71543937820ef
parentf5aa0ac6f70169f9d102c0dce86a70b7800cf8b0 (diff)
qcacld-2.0: Address kernel panic due to lock acquired in softirq context
prima to qcacld-2.0 propagation Observed kernel panic since the timer callback is invoked in soft-irq context if thread id is of wd thread as callback routine acquires locks through its execution. To address this issue, call the timer callback in process context by queuing the timer callback on a work queue. Change-Id: I82b52bc3f959f989519931e6ab7ac9e0defd32a5 CRs-Fixed: 999154
-rw-r--r--CORE/VOSS/inc/vos_timer.h3
-rw-r--r--CORE/VOSS/src/vos_api.c4
-rw-r--r--CORE/VOSS/src/vos_sched.h18
-rw-r--r--CORE/VOSS/src/vos_timer.c127
4 files changed, 151 insertions, 1 deletions
diff --git a/CORE/VOSS/inc/vos_timer.h b/CORE/VOSS/inc/vos_timer.h
index 1063b875f30c..181e6aecfc87 100644
--- a/CORE/VOSS/inc/vos_timer.h
+++ b/CORE/VOSS/inc/vos_timer.h
@@ -352,4 +352,7 @@ static inline uint32_t vos_system_ticks_to_msecs(vos_time_t ticks)
unsigned long vos_get_time_of_the_day_ms(void);
void vos_get_time_of_the_day_in_hr_min_sec_usec(char *tbuf, int len);
+void vos_process_wd_timer(void);
+void vos_wdthread_init_timer_work(void *callbackptr);
+void vos_wdthread_flush_timer_work(void);
#endif // #if !defined __VOSS_TIMER_H
diff --git a/CORE/VOSS/src/vos_api.c b/CORE/VOSS/src/vos_api.c
index 01e241b72bb0..aab204afe7f2 100644
--- a/CORE/VOSS/src/vos_api.c
+++ b/CORE/VOSS/src/vos_api.c
@@ -423,6 +423,8 @@ VOS_STATUS vos_open( v_CONTEXT_t *pVosContext, v_SIZE_t hddContextSize )
/* Initialize the timer module */
vos_timer_module_init();
+ vos_wdthread_init_timer_work(vos_process_wd_timer);
+
/* Initialize bug reporting structure */
vos_init_log_completion();
@@ -1259,6 +1261,8 @@ VOS_STATUS vos_close( v_CONTEXT_t vosContext )
vos_deinit_log_completion();
+ vos_wdthread_flush_timer_work();
+
return VOS_STATUS_SUCCESS;
}
diff --git a/CORE/VOSS/src/vos_sched.h b/CORE/VOSS/src/vos_sched.h
index a2669424068d..e1036f399914 100644
--- a/CORE/VOSS/src/vos_sched.h
+++ b/CORE/VOSS/src/vos_sched.h
@@ -322,6 +322,20 @@ struct vos_log_complete {
bool is_report_in_progress;
};
+/**
+ * struct vos_wdthread_timer_work - Watchdog timer thread structure
+ * @callback: Watchdog timer work call back
+ * @userdata: Input to the timer call back function
+ * @node: wdthread timer work Linklist
+ *
+ * This structure internally stores wdthread timer work related params
+ */
+struct vos_wdthread_timer_work {
+ vos_timer_callback_t callback;
+ void *userdata;
+ struct list_head node;
+};
+
typedef struct _VosContextType
{
/* Messages buffers */
@@ -397,6 +411,10 @@ typedef struct _VosContextType
/* radio index per driver */
int radio_index;
+ struct vos_wdthread_timer_work wdthread_timer_work;
+ struct list_head wdthread_timer_work_list;
+ struct work_struct wdthread_work;
+ spinlock_t wdthread_work_lock;
} VosContextType, *pVosContextType;
diff --git a/CORE/VOSS/src/vos_timer.c b/CORE/VOSS/src/vos_timer.c
index ef41710fceaa..a063c89e13a2 100644
--- a/CORE/VOSS/src/vos_timer.c
+++ b/CORE/VOSS/src/vos_timer.c
@@ -46,6 +46,7 @@
#include "wlan_qct_sys.h"
#include "vos_sched.h"
#include <linux/rtc.h>
+#include "vos_cnss.h"
/*--------------------------------------------------------------------------
Preprocessor definitions and constants
@@ -122,6 +123,9 @@ static void vos_linux_timer_callback (unsigned long data)
v_PVOID_t userData=NULL;
int threadId;
VOS_TIMER_TYPE type=VOS_TIMER_TYPE_SW;
+ v_CONTEXT_t vos_context = NULL;
+ pVosContextType vos_global_context;
+ struct vos_wdthread_timer_work *wdthread_timer_work;
VOS_ASSERT(timer);
@@ -190,7 +194,27 @@ static void vos_linux_timer_callback (unsigned long data)
if (vos_is_wd_thread(threadId)) {
VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_INFO,
"TIMER callback: running on wd thread");
- callback(userData);
+ vos_context = vos_get_global_context(VOS_MODULE_ID_HDD, NULL);
+ if (!vos_context) {
+ VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+ "%s: Global VOS context is Null", __func__);
+ return;
+ }
+ vos_global_context = (pVosContextType)vos_context;
+ wdthread_timer_work = vos_mem_malloc(sizeof(*wdthread_timer_work));
+ if (NULL == wdthread_timer_work) {
+ VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
+ "%s: No memory available", __func__);
+ return;
+ }
+ wdthread_timer_work->callback = callback;
+ wdthread_timer_work->userdata = userData;
+ spin_lock(&vos_global_context->wdthread_work_lock);
+ list_add(&wdthread_timer_work->node,
+ &vos_global_context->wdthread_timer_work_list);
+ spin_unlock(&vos_global_context->wdthread_work_lock);
+
+ schedule_work(&vos_global_context->wdthread_work);
return;
}
@@ -885,3 +909,104 @@ void vos_get_time_of_the_day_in_hr_min_sec_usec(char *tbuf, int len)
"[%02d:%02d:%02d.%06lu] ",
tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec);
}
+
+/**
+ * vos_wdthread_init_timer_work() - Initialize timer work
+ * @callbackptr: timer work callback
+ *
+ * Initialize watchdog thread timer work structure and linked
+ * list.
+ * return - void
+ */
+void vos_wdthread_init_timer_work(void *callbackptr)
+{
+ pVosContextType context;
+
+ context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
+ if (!context) {
+ VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+ "%s: Global VOS context is Null", __func__);
+ return;
+ }
+
+ spin_lock_init(&context->wdthread_work_lock);
+ INIT_LIST_HEAD(&context->wdthread_timer_work_list);
+ vos_init_work(&context->wdthread_work, callbackptr);
+}
+
+/**
+ * vos_wdthread_flush_timer_work() - Flush timer work
+ *
+ * Flush watchdog thread timer work structure.
+ * return - void
+ */
+void vos_wdthread_flush_timer_work()
+{
+ pVosContextType context;
+
+ context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
+ if (!context) {
+ VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+ "%s: Global VOS context is Null", __func__);
+ return;
+ }
+
+ vos_flush_work(&context->wdthread_work);
+}
+
+/**
+ * __vos_process_wd_timer() - Handle wathdog thread timer work
+ *
+ * Process watchdog thread timer work.
+ * return - void
+ */
+static void __vos_process_wd_timer(void)
+{
+ v_CONTEXT_t vos_context = NULL;
+ pVosContextType vos_global_context;
+ struct vos_wdthread_timer_work *wdthread_timer_work;
+ struct list_head *pos, *next;
+
+ vos_context = vos_get_global_context(VOS_MODULE_ID_SYS, NULL);
+
+ if (!vos_context) {
+ VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+ "%s: Global VOS context is Null", __func__);
+ return;
+ }
+
+ vos_global_context = (pVosContextType)vos_context;
+
+ spin_lock(&vos_global_context->wdthread_work_lock);
+ list_for_each_safe(pos, next,
+ &vos_global_context->wdthread_timer_work_list) {
+ wdthread_timer_work = list_entry(pos,
+ struct vos_wdthread_timer_work,
+ node);
+ list_del(pos);
+ spin_unlock(&vos_global_context->wdthread_work_lock);
+ if ((NULL != wdthread_timer_work->callback) &&
+ (NULL != wdthread_timer_work->userdata)) {
+ wdthread_timer_work->callback(
+ wdthread_timer_work->userdata);
+ }
+ vos_mem_free(wdthread_timer_work);
+ spin_lock(&vos_global_context->wdthread_work_lock);
+ }
+ spin_unlock(&vos_global_context->wdthread_work_lock);
+
+ return;
+}
+
+/**
+ * vos_process_wd_timer() - Wrapper function to handle timer work
+ *
+ * Wrapper function to process timer work.
+ * return - void
+ */
+void vos_process_wd_timer(void)
+{
+ vos_ssr_protect(__func__);
+ __vos_process_wd_timer();
+ vos_ssr_unprotect(__func__);
+}