summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArun Khandavalli <akhandav@qti.qualcomm.com>2016-04-27 18:47:50 +0530
committerGerrit - the friendly Code Review server <code-review@localhost>2016-05-10 03:49:25 -0700
commitc573bc0e4fd0b9c88adcfc48d0004d781266f07b (patch)
treea08aef2caf20d06671a6a748bddb7844f6d6a123
parent9e46ab1766a4bbc8773ddd9224317beeb4546f69 (diff)
qcacld-2.0: Prevent wlan exit in wrong state
When Wifi is turned off, then hdd_stop is called which leads to unload of driver. But before hdd_stop is called, user has turned on wifi and fwpath_change_handler is called before hdd_stop which leads to unload twice and leading to crash. To avoid this problem, 1) Syncronize the kickstart_driver call to keep wlan_hdd_inited updated. 2) Store the fwpath string locally and check the last mode with the current mode in fwpath_change_handler. - If load request comes and driver is already loaded with no change in fwpath string, then unload the driver and return failure. - If load request comes and driver is already loaded with change in fwpath string then unload and load the driver again. Change-Id: Ie59044c4f1bc9b40de9c3574607cb6d9a73caee3 CRs-Fixed: 1012569
-rw-r--r--CORE/HDD/src/wlan_hdd_main.c87
-rw-r--r--CORE/VOSS/src/vos_sched.c69
-rw-r--r--CORE/VOSS/src/vos_sched.h4
3 files changed, 145 insertions, 15 deletions
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index b8b7564604b5..5466e894d7fc 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -185,6 +185,7 @@ static int enable_dfs_chan_scan = -1;
#ifndef MODULE
static int wlan_hdd_inited;
+static char fwpath_mode_local[BUF_LEN];
#endif
/*
@@ -9139,8 +9140,18 @@ static inline int wlan_hdd_stop_can_enter_lowpower(hdd_adapter_t *adapter)
*/
static void kickstart_driver_handler(struct work_struct *work)
{
+ bool ready;
+
+ ready = vos_is_load_unload_ready(__func__);
+ if (!ready) {
+ VOS_ASSERT(0);
+ return;
+ }
+
+ vos_load_unload_protect(__func__);
hdd_driver_exit();
wlan_hdd_inited = 0;
+ vos_load_unload_unprotect(__func__);
}
static DECLARE_WORK(kickstart_driver_work, kickstart_driver_handler);
@@ -9148,6 +9159,7 @@ static DECLARE_WORK(kickstart_driver_work, kickstart_driver_handler);
/**
* kickstart_driver() - Initialize and Clean-up driver
* @load: True: initialize, False: Clean-up driver
+ * @mode_change: tell if last mode and current mode is same or not
*
* Delayed driver initialization when driver is statically linked and Clean-up
* when all the interfaces are down or any other condition which requires to
@@ -9159,12 +9171,13 @@ static DECLARE_WORK(kickstart_driver_work, kickstart_driver_handler);
*
* Return: 0 on success, non zero on failure
*/
-static int kickstart_driver(bool load)
+static int kickstart_driver(bool load, bool mode_change)
{
int ret_status;
- pr_info("%s: load: %d wlan_hdd_inited: %d, caller: %pf\n", __func__,
- load, wlan_hdd_inited, (void *)_RET_IP_);
+ pr_info("%s: load: %d wlan_hdd_inited: %d, mode_change: %d caller: %pf\n",
+ __func__, load, wlan_hdd_inited,
+ mode_change, (void *)_RET_IP_);
/* Make sure unload and load are synchronized */
flush_work(&kickstart_driver_work);
@@ -9185,12 +9198,20 @@ static int kickstart_driver(bool load)
return ret_status;
}
- hdd_driver_exit();
+ if (load && wlan_hdd_inited && !mode_change) {
+ /* Error condition */
+ hdd_driver_exit();
+ wlan_hdd_inited = 0;
+ ret_status = -EINVAL;
+ } else {
+ hdd_driver_exit();
- msleep(200);
+ msleep(200);
+
+ ret_status = hdd_driver_init();
+ wlan_hdd_inited = ret_status ? 0 : 1;
+ }
- ret_status = hdd_driver_init();
- wlan_hdd_inited = ret_status ? 0 : 1;
return ret_status;
}
@@ -9203,11 +9224,22 @@ static int kickstart_driver(bool load)
*/
static inline void wlan_hdd_stop_enter_lowpower(hdd_context_t *hdd_ctx)
{
+ bool ready;
+
/* Do not clean up n/w ifaces if we are in DRIVER STOP phase or else
* DRIVER START will fail and Wi-Fi will not resume successfully
*/
- if (hdd_ctx && !hdd_ctx->driver_being_stopped)
- kickstart_driver(false);
+ if (hdd_ctx && !hdd_ctx->driver_being_stopped) {
+ ready = vos_is_load_unload_ready(__func__);
+ if (!ready) {
+ VOS_ASSERT(0);
+ return;
+ }
+
+ vos_load_unload_protect(__func__);
+ kickstart_driver(false, false);
+ vos_load_unload_unprotect(__func__);
+ }
}
/**
@@ -15853,12 +15885,37 @@ static int con_mode_handler(const char *kmessage,
static int fwpath_changed_handler(const char *kmessage,
struct kernel_param *kp)
{
- int ret;
+ int ret;
+ bool mode_change;
- ret = param_set_copystring(kmessage, kp);
- if (0 == ret)
- ret = kickstart_driver(true);
- return ret;
+ ret = param_set_copystring(kmessage, kp);
+
+ if (!ret) {
+ bool ready;
+
+ ret = strncmp(fwpath_mode_local, kmessage , 3);
+ mode_change = ret ? true : false;
+
+
+ pr_info("%s : new_mode : %s, present_mode : %s\n", __func__,
+ kmessage, fwpath_mode_local);
+
+ strlcpy(fwpath_mode_local, kmessage,
+ sizeof(fwpath_mode_local));
+
+ ready = vos_is_load_unload_ready(__func__);
+
+ if (!ready) {
+ VOS_ASSERT(0);
+ return -EINVAL;
+ }
+
+ vos_load_unload_protect(__func__);
+ ret = kickstart_driver(true, mode_change);
+ vos_load_unload_unprotect(__func__);
+ }
+
+ return ret;
}
#if ! defined(QCA_WIFI_FTM)
@@ -15881,7 +15938,7 @@ static int con_mode_handler(const char *kmessage, struct kernel_param *kp)
ret = param_set_int(kmessage, kp);
if (0 == ret)
- ret = kickstart_driver(true);
+ ret = kickstart_driver(true, false);
return ret;
}
#endif
diff --git a/CORE/VOSS/src/vos_sched.c b/CORE/VOSS/src/vos_sched.c
index a15a6acba23b..b76963a44f8f 100644
--- a/CORE/VOSS/src/vos_sched.c
+++ b/CORE/VOSS/src/vos_sched.c
@@ -67,16 +67,19 @@
#define VOS_SCHED_THREAD_HEART_BEAT INFINITE
/* Milli seconds to delay SSR thread when an Entry point is Active */
#define SSR_WAIT_SLEEP_TIME 200
+#define LOAD_UNLOAD_WAIT_SLEEP_TIME 200
/* MAX iteration count to wait for Entry point to exit before
* we proceed with SSR in WD Thread
*/
#define MAX_SSR_WAIT_ITERATIONS 200
+#define MAX_LOAD_UNLOAD_WAIT_ITERATIONS 50
#define MAX_SSR_PROTECT_LOG (16)
/* Timer value for detecting thread stuck issues */
#define THREAD_STUCK_TIMER_VAL 5000 /* 5 seconds */
static atomic_t ssr_protect_entry_count;
+static atomic_t load_unload_protect_count;
struct ssr_protect {
const char* func;
@@ -2017,6 +2020,72 @@ static void vos_print_external_threads(void)
/**
+ * vos_is_load_unload_ready() - check load/unload ready
+ * @caller_func: Pointer to caller function
+ *
+ * This function will check if calling execution can call
+ * kickstart driver for load/unload
+ *
+ * Return: true if ready else false.
+ */
+bool vos_is_load_unload_ready(const char *caller_func)
+{
+ int count = MAX_LOAD_UNLOAD_WAIT_ITERATIONS;
+
+ while (count) {
+ if (!atomic_read(&load_unload_protect_count))
+ break;
+
+ if (--count) {
+ VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
+ "%s: Waiting for load/unload active entry points to exit",
+ __func__);
+ msleep(LOAD_UNLOAD_WAIT_SLEEP_TIME);
+ }
+ }
+ /* at least one external thread is executing */
+ if (!count) {
+ VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_ERROR,
+ "%s : Thread stuck for load/unload", __func__);
+ return false;
+ }
+
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+ "Allowing load/Unload for %s", caller_func);
+
+ return true;
+}
+
+
+/**
+ * vos_load_unload_protect () - Protect load/Unload
+ * @caller_func : Pointer to caller function
+ *
+ * This function will protect the atomic variable by incrementing
+ * its value
+ *
+ * Return: void
+ */
+
+void vos_load_unload_protect(const char *caller_func)
+{
+ atomic_inc(&load_unload_protect_count);
+}
+
+/**
+ * vos_load_unload_unprotect () - Unprotect load/unload
+ * @caller_func : Pointer to caller_func
+ *
+ * This function will decrement the atomic variable value
+ *
+ * Return: void
+ */
+void vos_load_unload_unprotect(const char *caller_func)
+{
+ atomic_dec(&load_unload_protect_count);
+}
+
+/**
@brief vos_ssr_protect()
This function is called to keep track of active driver entry points
diff --git a/CORE/VOSS/src/vos_sched.h b/CORE/VOSS/src/vos_sched.h
index 974e2cb0b804..733da4f60a53 100644
--- a/CORE/VOSS/src/vos_sched.h
+++ b/CORE/VOSS/src/vos_sched.h
@@ -637,6 +637,10 @@ void vos_ssr_protect_init(void);
void vos_ssr_protect(const char *caller_func);
void vos_ssr_unprotect(const char *caller_func);
bool vos_is_ssr_ready(const char *caller_func);
+
+void vos_load_unload_protect(const char *caller_func);
+void vos_load_unload_unprotect(const char *caller_func);
+bool vos_is_load_unload_ready(const char *caller_func);
int vos_get_gfp_flags(void);
void vos_wd_reset_thread_stuck_count(int thread_id);
bool vos_is_wd_thread(int thread_id);