diff options
| author | Arun Khandavalli <akhandav@qti.qualcomm.com> | 2016-04-27 18:47:50 +0530 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2016-05-10 03:49:25 -0700 |
| commit | c573bc0e4fd0b9c88adcfc48d0004d781266f07b (patch) | |
| tree | a08aef2caf20d06671a6a748bddb7844f6d6a123 | |
| parent | 9e46ab1766a4bbc8773ddd9224317beeb4546f69 (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.c | 87 | ||||
| -rw-r--r-- | CORE/VOSS/src/vos_sched.c | 69 | ||||
| -rw-r--r-- | CORE/VOSS/src/vos_sched.h | 4 |
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); |
