From be8850b4c67ec4c302e0621ddf487a5f989c1d4f Mon Sep 17 00:00:00 2001 From: Nachiket Kukade Date: Mon, 18 Sep 2017 15:37:00 +0530 Subject: [PATCH] qcacld-3.0: Set up watchdog timers for pld driver ops Requirement to detect if a PLD OP has stuck for a long time. Define a timer for 30 seconds and let it run during pld ops probe, reinit, remove and shutdown. Upon expiry print important debug information then either trigger recovery or BUG ON. Change-Id: I6c132cb71917cec7dead9c6c91ce6778e20bffe7 CRs-Fixed: 2090785 --- core/hdd/inc/wlan_hdd_main.h | 36 +++++++++++++++ core/hdd/src/wlan_hdd_driver_ops.c | 15 +++++++ core/hdd/src/wlan_hdd_main.c | 71 ++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h index 294116737eea..39c29f1e38ed 100644 --- a/core/hdd/inc/wlan_hdd_main.h +++ b/core/hdd/inc/wlan_hdd_main.h @@ -1950,6 +1950,19 @@ struct hdd_channel_info { u_int8_t vht_center_freq_seg1; }; +/* + * @eHDD_DRV_OP_PROBE: Refers to .probe operation + * @eHDD_DRV_OP_REMOVE: Refers to .remove operation + * @eHDD_DRV_OP_SHUTDOWN: Refers to .shutdown operation + * @eHDD_DRV_OP_REINIT: Refers to .reinit operation + */ +enum { + eHDD_DRV_OP_PROBE = 0, + eHDD_DRV_OP_REMOVE, + eHDD_DRV_OP_SHUTDOWN, + eHDD_DRV_OP_REINIT +}; + /* * Function declarations and documentation */ @@ -2872,4 +2885,27 @@ void hdd_dp_trace_init(struct hdd_config *config); int hdd_set_limit_off_chan_for_tos(struct hdd_adapter *adapter, enum tos tos, bool is_tos_active); +/** + * hdd_drv_ops_inactivity_handler() - Timeout handler for driver ops + * inactivity timer + * + * Return: None + */ +void hdd_drv_ops_inactivity_handler(void); + +/** + * hdd_start_driver_ops_timer() - Starts driver ops inactivity timer + * @drv_op: Enum indicating driver op + * + * Return: none + */ +void hdd_start_driver_ops_timer(int drv_op); + +/** + * hdd_stop_driver_ops_timer() - Stops driver ops inactivity timer + * + * Return: none + */ +void hdd_stop_driver_ops_timer(void); + #endif /* end #if !defined(WLAN_HDD_MAIN_H) */ diff --git a/core/hdd/src/wlan_hdd_driver_ops.c b/core/hdd/src/wlan_hdd_driver_ops.c index a682cb027984..e3dfe02f2940 100644 --- a/core/hdd/src/wlan_hdd_driver_ops.c +++ b/core/hdd/src/wlan_hdd_driver_ops.c @@ -334,6 +334,11 @@ static int wlan_hdd_probe(struct device *dev, void *bdev, reinit ? "re-" : "", QWLAN_VERSIONSTR); mutex_lock(&hdd_init_deinit_lock); + if (!reinit) + hdd_start_driver_ops_timer(eHDD_DRV_OP_PROBE); + else + hdd_start_driver_ops_timer(eHDD_DRV_OP_REINIT); + hdd_prevent_suspend(WIFI_POWER_EVENT_WAKELOCK_DRIVER_INIT); /* @@ -378,6 +383,7 @@ static int wlan_hdd_probe(struct device *dev, void *bdev, cds_set_driver_in_bad_state(false); probe_fail_cnt = 0; re_init_fail_cnt = 0; + hdd_stop_driver_ops_timer(); mutex_unlock(&hdd_init_deinit_lock); return 0; @@ -399,6 +405,7 @@ err_hdd_deinit: hdd_remove_pm_qos(dev); cds_set_fw_down(false); + hdd_stop_driver_ops_timer(); mutex_unlock(&hdd_init_deinit_lock); return ret; } @@ -1153,7 +1160,11 @@ static void wlan_hdd_pld_remove(struct device *dev, { ENTER(); mutex_lock(&hdd_init_deinit_lock); + hdd_start_driver_ops_timer(eHDD_DRV_OP_REMOVE); + wlan_hdd_remove(dev); + + hdd_stop_driver_ops_timer(); mutex_unlock(&hdd_init_deinit_lock); EXIT(); } @@ -1170,7 +1181,11 @@ static void wlan_hdd_pld_shutdown(struct device *dev, { ENTER(); mutex_lock(&hdd_init_deinit_lock); + hdd_start_driver_ops_timer(eHDD_DRV_OP_SHUTDOWN); + wlan_hdd_shutdown(); + + hdd_stop_driver_ops_timer(); mutex_unlock(&hdd_init_deinit_lock); EXIT(); } diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 2ef9a2c838e9..96ce3f9f30ce 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -174,6 +174,12 @@ static struct attribute *attrs[] = { #define MODULE_INITIALIZED 1 #endif +#define HDD_OPS_INACTIVITY_TIMEOUT (120000) +#define MAX_OPS_NAME_STRING_SIZE 20 + +static qdf_timer_t hdd_drv_ops_inactivity_timer; +static char drv_ops_string[MAX_OPS_NAME_STRING_SIZE]; + /* the Android framework expects this param even though we don't use it */ #define BUF_LEN 20 static char fwpath_buffer[BUF_LEN]; @@ -10987,6 +10993,10 @@ int hdd_init(void) goto err_out; } + qdf_timer_init(NULL, &hdd_drv_ops_inactivity_timer, + (void *)hdd_drv_ops_inactivity_handler, NULL, + QDF_TIMER_TYPE_SW); + hdd_trace_init(); hdd_qdf_print_init(); @@ -11006,6 +11016,7 @@ err_out: void hdd_deinit(void) { hdd_deinit_wowl(); + qdf_timer_free(&hdd_drv_ops_inactivity_timer); hdd_qdf_print_deinit(); @@ -12668,6 +12679,66 @@ int hdd_set_limit_off_chan_for_tos(struct hdd_adapter *adapter, enum tos tos, return ret; } +/** + * hdd_start_driver_ops_timer() - Starts driver ops inactivity timer + * @drv_op: Enum indicating driver op + * + * Return: none + */ +void hdd_start_driver_ops_timer(int drv_op) +{ + memset(drv_ops_string, 0, MAX_OPS_NAME_STRING_SIZE); + switch (drv_op) { + case eHDD_DRV_OP_PROBE: + memcpy(drv_ops_string, "probe", sizeof("probe")); + break; + case eHDD_DRV_OP_REMOVE: + memcpy(drv_ops_string, "remove", sizeof("remove")); + break; + case eHDD_DRV_OP_SHUTDOWN: + memcpy(drv_ops_string, "shutdown", sizeof("shutdown")); + break; + case eHDD_DRV_OP_REINIT: + memcpy(drv_ops_string, "reinit", sizeof("reinit")); + break; + } + + qdf_timer_start(&hdd_drv_ops_inactivity_timer, + HDD_OPS_INACTIVITY_TIMEOUT); +} + +/** + * hdd_stop_driver_ops_timer() - Stops driver ops inactivity timer + * + * Return: none + */ +void hdd_stop_driver_ops_timer(void) +{ + qdf_timer_sync_cancel(&hdd_drv_ops_inactivity_timer); +} + +/** + * hdd_drv_ops_inactivity_handler() - Timeout handler for driver ops + * inactivity timer + * + * Return: None + */ +void hdd_drv_ops_inactivity_handler(void) +{ + hdd_err("%s: %d Sec timer expired while in .%s", + __func__, HDD_OPS_INACTIVITY_TIMEOUT/1000, drv_ops_string); + + /* Driver shutdown is stuck, no recovery possible at this point */ + if (0 == qdf_mem_cmp(&drv_ops_string[0], "shutdown", + sizeof("shutdown"))) + QDF_BUG(0); + + if (cds_is_self_recovery_enabled()) + cds_trigger_recovery(); + else + QDF_BUG(0); +} + /* Register the module init/exit functions */ module_init(hdd_module_init); module_exit(hdd_module_exit);