diff --git a/core/cds/inc/cds_api.h b/core/cds/inc/cds_api.h index 1f0a6a323151..a9401b6ad2c4 100644 --- a/core/cds/inc/cds_api.h +++ b/core/cds/inc/cds_api.h @@ -345,13 +345,7 @@ bool cds_is_packet_log_enabled(void); uint64_t cds_get_monotonic_boottime(void); -/** - * cds_trigger_recovery_wrapper() - a temp wrapper to trigger self recovery - * - * Return: none - */ -void cds_trigger_recovery_wrapper(void); -void cds_trigger_recovery(bool); +void cds_trigger_recovery(void); void cds_set_wakelock_logging(bool value); bool cds_is_wakelock_enabled(void); diff --git a/core/cds/src/cds_api.c b/core/cds/src/cds_api.c index 021f38d0fe11..cbbc255c8bff 100644 --- a/core/cds/src/cds_api.c +++ b/core/cds/src/cds_api.c @@ -138,7 +138,7 @@ v_CONTEXT_t cds_init(void) qdf_lock_stats_init(); qdf_mem_init(); qdf_mc_timer_manager_init(); - qdf_register_self_recovery_callback(cds_trigger_recovery_wrapper); + qdf_register_self_recovery_callback(cds_trigger_recovery); gp_cds_context = &g_cds_context; @@ -1713,97 +1713,134 @@ bool cds_is_packet_log_enabled(void) return pHddCtx->config->enablePacketLog; } -/** - * cds_config_recovery_work() - configure self recovery - * @qdf_ctx: pointer of qdf context - * - * Return: none - */ - -static void cds_config_recovery_work(qdf_device_t qdf_ctx) +static int cds_force_assert_target_via_pld(qdf_device_t qdf) { - if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { - QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, - "Recovery is in progress, ignore!"); - } else { - cds_set_recovery_in_progress(true); - QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, - "schedule recovery work!"); - pld_schedule_recovery_work(qdf_ctx->dev, - PLD_REASON_DEFAULT); - } + int errno; + + errno = pld_force_assert_target(qdf->dev); + if (errno == -EOPNOTSUPP) + cds_info("PLD does not support target force assert"); + else if (errno) + cds_err("Failed PLD target force assert; errno %d", errno); + else + cds_info("Target force assert triggered via PLD"); + + return errno; } -void cds_trigger_recovery_wrapper(void) +static QDF_STATUS cds_force_assert_target_via_wmi(qdf_device_t qdf) { - cds_trigger_recovery(false); + QDF_STATUS status; + t_wma_handle *wma; + + wma = cds_get_context(QDF_MODULE_ID_WMA); + if (!wma) { + cds_err("wma is null"); + return QDF_STATUS_E_INVAL; + } + + status = wma_crash_inject(wma, RECOVERY_SIM_SELF_RECOVERY, 0); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed target force assert; status %d", status); + return status; + } + + status = qdf_wait_single_event(&wma->recovery_event, + WMA_CRASH_INJECT_TIMEOUT); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed target force assert wait; status %d", status); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * cds_force_assert_target() - Send assert command to firmware + * @qdf: QDF device instance to assert + * + * An out-of-band recovery mechanism will cleanup and restart the entire wlan + * subsystem in the event of a firmware crash. This API injects a firmware + * crash to start this process when the wlan driver is known to be in a bad + * state. If a firmware assert inject fails, the wlan driver will schedule + * the driver recovery anyway, as a best effort attempt to return to a working + * state. + * + * Return: QDF_STATUS + */ +static QDF_STATUS cds_force_assert_target(qdf_device_t qdf) +{ + int errno; + QDF_STATUS status; + + /* first, try target assert inject via pld */ + errno = cds_force_assert_target_via_pld(qdf); + if (!errno) + return QDF_STATUS_SUCCESS; + if (errno != -EOPNOTSUPP) + return QDF_STATUS_E_FAILURE; + + /* pld assert is not supported, try target assert inject via wmi */ + status = cds_force_assert_target_via_wmi(qdf); + if (QDF_IS_STATUS_SUCCESS(status)) + return QDF_STATUS_SUCCESS; + + /* wmi assert failed, start recovery without the firmware assert */ + cds_err("Scheduling recovery work without firmware assert"); + cds_set_recovery_in_progress(true); + pld_schedule_recovery_work(qdf->dev, PLD_REASON_DEFAULT); + + return status; } /** * cds_trigger_recovery() - trigger self recovery - * @skip_crash_inject: Boolean value to skip to send crash inject cmd * * Return: none */ -void cds_trigger_recovery(bool skip_crash_inject) +void cds_trigger_recovery(void) { - tp_wma_handle wma_handle = cds_get_context(QDF_MODULE_ID_WMA); - QDF_STATUS status = QDF_STATUS_SUCCESS; - qdf_runtime_lock_t recovery_lock; - qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + QDF_STATUS status; + qdf_runtime_lock_t rtl; + qdf_device_t qdf; - if (!wma_handle) { - QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, - "WMA context is invalid!"); - return; - } - if (!qdf_ctx) { - QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, - "QDF context is invalid!"); + if (!cds_is_self_recovery_enabled()) { + cds_err("Recovery is not enabled"); + QDF_BUG(0); return; } - status = qdf_runtime_lock_init(&recovery_lock); - if (QDF_STATUS_SUCCESS != status) { - QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, - "Could not acquire runtime pm lock: %d!", status); + if (cds_is_driver_recovering() || cds_is_driver_in_bad_state()) { + cds_err("Recovery in progress; ignoring recovery trigger"); return; } - qdf_runtime_pm_prevent_suspend(&recovery_lock); - - /* - * If force assert thru platform is available, trigger that interface. - * That should generate recovery by going thru the normal FW - * assert recovery model. - */ - if (!pld_force_assert_target(qdf_ctx->dev)) { - QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH, - "Force assert triggered"); - goto out; + qdf = cds_get_context(QDF_MODULE_ID_QDF_DEVICE); + if (!qdf) { + cds_err("Qdf context is null"); + return; } - QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_INFO_HIGH, - "Force assert not available at platform"); - - if (!skip_crash_inject) { - - wma_crash_inject(wma_handle, RECOVERY_SIM_SELF_RECOVERY, 0); - status = qdf_wait_single_event(&wma_handle->recovery_event, - WMA_CRASH_INJECT_TIMEOUT); - - if (QDF_STATUS_SUCCESS != status) { - QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR, - "CRASH_INJECT command is timed out!"); - cds_config_recovery_work(qdf_ctx); - } - } else { - cds_config_recovery_work(qdf_ctx); + status = qdf_runtime_lock_init(&rtl); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed to initialize runtime pm lock"); + return; } -out: - qdf_runtime_pm_allow_suspend(&recovery_lock); - qdf_runtime_lock_deinit(&recovery_lock); + status = qdf_runtime_pm_prevent_suspend(&rtl); + if (QDF_IS_STATUS_ERROR(status)) { + cds_err("Failed to acquire runtime pm lock"); + goto deinit_rtl; + } + + cds_force_assert_target(qdf); + + status = qdf_runtime_pm_allow_suspend(&rtl); + if (QDF_IS_STATUS_ERROR(status)) + cds_err("Failed to release runtime pm lock"); + +deinit_rtl: + qdf_runtime_lock_deinit(&rtl); } /** diff --git a/core/dp/txrx/ol_txrx.c b/core/dp/txrx/ol_txrx.c index 2e1f71320494..da3b1fd14aec 100644 --- a/core/dp/txrx/ol_txrx.c +++ b/core/dp/txrx/ol_txrx.c @@ -2504,21 +2504,11 @@ ol_txrx_peer_attach(struct cdp_vdev *pvdev, uint8_t *peer_mac_addr) rc = qdf_wait_single_event(&vdev->wait_delete_comp, PEER_DELETION_TIMEOUT); if (QDF_STATUS_SUCCESS != rc) { - ol_txrx_err( - "error waiting for peer(%d) deletion, status %d\n", - vdev->wait_on_peer_id, (int) rc); - if (cds_is_self_recovery_enabled()) - cds_trigger_recovery(false); - else - - /* - * Add equivalent of following line when it - * becomes available. - * wma_peer_debug_dump(); - */ - QDF_ASSERT(0); - + ol_txrx_err("error waiting for peer(%d) deletion, status %d\n", + vdev->wait_on_peer_id, (int) rc); + cds_trigger_recovery(); vdev->wait_on_peer_id = OL_TXRX_INVALID_LOCAL_PEER_ID; + return NULL; } } @@ -3407,19 +3397,7 @@ static QDF_STATUS ol_txrx_clear_peer(struct cdp_pdev *ppdev, uint8_t sta_id) void peer_unmap_timer_work_function(void *param) { WMA_LOGE("Enter: %s", __func__); - /* - * wma_peer_debug_dump() will be replaced with a new routine. - * Add the equivalent of wma_peer_debug_dump() when available. - */ - if (cds_is_self_recovery_enabled()) { - if (!cds_is_driver_recovering()) - cds_trigger_recovery(false); - else - WMA_LOGE("%s: Recovery is in progress, ignore!", - __func__); - } else { - QDF_BUG(0); - } + cds_trigger_recovery(); } /** diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c index dd2ad810a82f..e8e176c54607 100644 --- a/core/hdd/src/wlan_hdd_hostapd.c +++ b/core/hdd/src/wlan_hdd_hostapd.c @@ -2645,7 +2645,7 @@ static int __iw_softap_set_two_ints_getnone(struct net_device *dev, hdd_err("WE_SET_FW_CRASH_INJECT: %d %d", value[1], value[2]); if (value[1] == 3) { - cds_trigger_recovery(false); + cds_trigger_recovery(); return 0; } ret = wma_cli_set2_command(adapter->sessionId, diff --git a/core/hdd/src/wlan_hdd_scan.c b/core/hdd/src/wlan_hdd_scan.c index 879373315f94..e06f6163d76e 100644 --- a/core/hdd/src/wlan_hdd_scan.c +++ b/core/hdd/src/wlan_hdd_scan.c @@ -144,11 +144,8 @@ static void hdd_scan_inactivity_timer_handler(void *scan_req) else if (cds_is_driver_in_bad_state()) hdd_err("%s: Module in bad state; Ignore hdd scan req timeout", __func__); - else if (cds_is_self_recovery_enabled()) - cds_trigger_recovery(false); else - QDF_BUG(0); - + cds_trigger_recovery(); } /** @@ -919,13 +916,9 @@ static int __wlan_hdd_cfg80211_scan(struct wiphy *wiphy, WLAN_LOG_REASON_SCAN_NOT_ALLOWED, false, pHddCtx->config->enableSelfRecovery); - } else if (pHddCtx->config-> - enableSelfRecovery) { - hdd_err("Triggering SSR due to scan stuck"); - cds_trigger_recovery(false); } else { - hdd_err("QDF_BUG due to scan stuck"); - QDF_BUG(0); + hdd_err("Triggering SSR due to scan stuck"); + cds_trigger_recovery(); } } } diff --git a/core/hdd/src/wlan_hdd_wext.c b/core/hdd/src/wlan_hdd_wext.c index 9eab62cd62cb..e4b17ad9a3be 100644 --- a/core/hdd/src/wlan_hdd_wext.c +++ b/core/hdd/src/wlan_hdd_wext.c @@ -12821,7 +12821,7 @@ static int __iw_set_two_ints_getnone(struct net_device *dev, pr_err("SSR is triggered by iwpriv CRASH_INJECT: %d %d\n", value[1], value[2]); if (value[1] == 3) { - cds_trigger_recovery(false); + cds_trigger_recovery(); return 0; } ret = wma_cli_set2_command(pAdapter->sessionId, diff --git a/core/pld/src/pld_common.c b/core/pld/src/pld_common.c index 27d09a5f5639..65e24abe782a 100644 --- a/core/pld/src/pld_common.c +++ b/core/pld/src/pld_common.c @@ -1538,25 +1538,19 @@ int pld_is_qmi_disable(struct device *dev) */ int pld_force_assert_target(struct device *dev) { - int ret = 0; enum pld_bus_type type = pld_get_bus_type(dev); switch (type) { case PLD_BUS_TYPE_SNOC: - ret = pld_snoc_force_assert_target(dev); - break; + return pld_snoc_force_assert_target(dev); case PLD_BUS_TYPE_PCIE: - ret = pld_pcie_force_assert_target(dev); - break; + return pld_pcie_force_assert_target(dev); case PLD_BUS_TYPE_SDIO: - ret = -EINVAL; - break; + return -EINVAL; default: pr_err("Invalid device type %d\n", type); - ret = -EINVAL; - break; + return -EINVAL; } - return ret; } /** diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c index f377a47edb9e..ad7e4cdda23f 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -507,10 +507,8 @@ tSmeCmd *sme_get_command_buffer(tpAniSirGlobal pMac) WLAN_LOG_REASON_SME_OUT_OF_CMD_BUF, false, pMac->sme.enableSelfRecovery ? true : false); - else if (pMac->sme.enableSelfRecovery) - cds_trigger_recovery(false); else - QDF_BUG(0); + cds_trigger_recovery(); } /* memset to zero */ diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c index 1ea2ce88ce97..771cd3419db4 100644 --- a/core/wma/src/wma_features.c +++ b/core/wma/src/wma_features.c @@ -3903,16 +3903,8 @@ static inline void wma_suspend_target_timeout(bool is_self_recovery_enabled) if (cds_is_load_or_unload_in_progress()) WMA_LOGE("%s: Module (un)loading; Ignoring suspend timeout", __func__); - else if (cds_is_driver_recovering()) - WMA_LOGE("%s: Module recovering; Ignoring suspend timeout", - __func__); - else if (cds_is_driver_in_bad_state()) - WMA_LOGE("%s: Module in bad state; Ignoring suspend timeout", - __func__); - else if (is_self_recovery_enabled) - cds_trigger_recovery(false); else - QDF_BUG(0); + cds_trigger_recovery(); } #ifdef FEATURE_WLAN_TDLS