diff --git a/core/cds/inc/cds_api.h b/core/cds/inc/cds_api.h index 56d43fac9f8c..fb7318236c14 100644 --- a/core/cds/inc/cds_api.h +++ b/core/cds/inc/cds_api.h @@ -354,7 +354,28 @@ bool cds_is_packet_log_enabled(void); uint64_t cds_get_monotonic_boottime(void); -void cds_trigger_recovery(void); +/** + * cds_get_recovery_reason() - get self recovery reason + * @reason: cds hang reason + * + * Return: None + */ +void cds_get_recovery_reason(enum qdf_hang_reason *reason); + +/** + * cds_reset_recovery_reason() - reset the reason to unspecified + * + * Return: None + */ +void cds_reset_recovery_reason(void); + +/** + * cds_trigger_recovery() - trigger self recovery + * @reason: recovery reason + * + * Return: none + */ +void cds_trigger_recovery(enum qdf_hang_reason reason); void cds_set_wakelock_logging(bool value); bool cds_is_wakelock_enabled(void); diff --git a/core/cds/inc/cds_sched.h b/core/cds/inc/cds_sched.h index e6ee6fabebd2..e53887ea36a0 100644 --- a/core/cds/inc/cds_sched.h +++ b/core/cds/inc/cds_sched.h @@ -230,6 +230,7 @@ typedef struct _cds_context_type { struct ol_tx_sched_wrr_ac_specs_t ac_specs[TX_WMM_AC_NUM]; qdf_work_t cds_recovery_work; qdf_workqueue_t *cds_recovery_wq; + enum qdf_hang_reason recovery_reason; } cds_context_type, *p_cds_contextType; /*--------------------------------------------------------------------------- diff --git a/core/cds/src/cds_api.c b/core/cds/src/cds_api.c index 8a9facb1465c..b6baa949c32b 100644 --- a/core/cds/src/cds_api.c +++ b/core/cds/src/cds_api.c @@ -1848,13 +1848,51 @@ deinit_rtl: qdf_runtime_lock_deinit(&rtl); } +/** + * cds_get_recovery_reason() - get self recovery reason + * @reason: recovery reason + * + * Return: None + */ +void cds_get_recovery_reason(enum qdf_hang_reason *reason) +{ + if (!gp_cds_context) { + cds_err("gp_cds_context is null"); + return; + } + + *reason = gp_cds_context->recovery_reason; +} + +/** + * cds_reset_recovery_reason() - reset the reason to unspecified + * + * Return: None + */ +void cds_reset_recovery_reason(void) +{ + if (!gp_cds_context) { + cds_err("gp_cds_context is null"); + return; + } + + gp_cds_context->recovery_reason = QDF_REASON_UNSPECIFIED; +} + /** * cds_trigger_recovery() - trigger self recovery + * @reason: recovery reason * * Return: none */ -void cds_trigger_recovery(void) +void cds_trigger_recovery(enum qdf_hang_reason reason) { + if (!gp_cds_context) { + cds_err("gp_cds_context is null"); + return; + } + + gp_cds_context->recovery_reason = reason; if (in_atomic()) { qdf_queue_work(0, gp_cds_context->cds_recovery_wq, &gp_cds_context->cds_recovery_work); diff --git a/core/dp/txrx/ol_txrx.c b/core/dp/txrx/ol_txrx.c index 070bbd5adf4e..5c8986ed85cd 100644 --- a/core/dp/txrx/ol_txrx.c +++ b/core/dp/txrx/ol_txrx.c @@ -2463,6 +2463,9 @@ static void ol_txrx_flush_cache_rx_queue(void) } } +/* Define short name to use in cds_trigger_recovery */ +#define PEER_DEL_TIMEOUT QDF_PEER_DELETION_TIMEDOUT + /** * ol_txrx_peer_attach - Allocate and set up references for a * data peer object. @@ -2566,7 +2569,7 @@ ol_txrx_peer_attach(struct cdp_vdev *pvdev, uint8_t *peer_mac_addr) if (QDF_STATUS_SUCCESS != rc) { ol_txrx_err("error waiting for peer(%d) deletion, status %d\n", vdev->wait_on_peer_id, (int) rc); - cds_trigger_recovery(); + cds_trigger_recovery(PEER_DEL_TIMEOUT); vdev->wait_on_peer_id = OL_TXRX_INVALID_LOCAL_PEER_ID; return NULL; @@ -2673,6 +2676,8 @@ ol_txrx_peer_attach(struct cdp_vdev *pvdev, uint8_t *peer_mac_addr) return (void *)peer; } +#undef PEER_DEL_TIMEOUT + /* * Discarding tx filter - removes all data frames (disconnected state) */ @@ -3457,7 +3462,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__); - cds_trigger_recovery(); + cds_trigger_recovery(QDF_PEER_UNMAP_TIMEDOUT); } /** diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index e531dd5a8528..3ae695527f8d 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -857,6 +857,105 @@ int wlan_hdd_send_avoid_freq_event(struct hdd_context *hdd_ctx, return 0; } +/* + * define short names for the global vendor params + * used by QCA_NL80211_VENDOR_SUBCMD_HANG + */ +#define HANG_REASON_INDEX QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX + +/** + * hdd_convert_hang_reason() - Convert cds recovery reason to vendor specific + * hang reason + * @reason: cds recovery reason + * + * Return: Vendor specific reason code + */ +static enum qca_wlan_vendor_hang_reason +hdd_convert_hang_reason(enum qdf_hang_reason reason) +{ + uint32_t ret_val; + + switch (reason) { + case QDF_RX_HASH_NO_ENTRY_FOUND: + ret_val = QCA_WLAN_HANG_RX_HASH_NO_ENTRY_FOUND; + break; + case QDF_PEER_DELETION_TIMEDOUT: + ret_val = QCA_WLAN_HANG_PEER_DELETION_TIMEDOUT; + break; + case QDF_PEER_UNMAP_TIMEDOUT: + ret_val = QCA_WLAN_HANG_PEER_UNMAP_TIMEDOUT; + break; + case QDF_SCAN_REQ_EXPIRED: + ret_val = QCA_WLAN_HANG_SCAN_REQ_EXPIRED; + break; + case QDF_SCAN_ATTEMPT_FAILURES: + ret_val = QCA_WLAN_HANG_SCAN_ATTEMPT_FAILURES; + break; + case QDF_GET_MSG_BUFF_FAILURE: + ret_val = QCA_WLAN_HANG_GET_MSG_BUFF_FAILURE; + break; + case QDF_ACTIVE_LIST_TIMEOUT: + ret_val = QCA_WLAN_HANG_ACTIVE_LIST_TIMEOUT; + break; + case QDF_SUSPEND_TIMEOUT: + ret_val = QCA_WLAN_HANG_SUSPEND_TIMEOUT; + break; + case QDF_RESUME_TIMEOUT: + ret_val = QCA_WLAN_HANG_RESUME_TIMEOUT; + break; + case QDF_REASON_UNSPECIFIED: + default: + ret_val = QCA_WLAN_HANG_REASON_UNSPECIFIED; + } + return ret_val; +} + +/** + * wlan_hdd_send_hang_reason_event() - Send hang reason to the userspace + * @hdd_ctx: Pointer to hdd context + * @reason: cds recovery reason + * + * Return: 0 on success or failure reason + */ +int wlan_hdd_send_hang_reason_event(struct hdd_context *hdd_ctx, + enum qdf_hang_reason reason) +{ + struct sk_buff *vendor_event; + enum qca_wlan_vendor_hang_reason hang_reason; + + ENTER(); + + if (!hdd_ctx) { + hdd_err("HDD context is null"); + return -EINVAL; + } + + vendor_event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, + NULL, + sizeof(uint32_t), + HANG_REASON_INDEX, + GFP_KERNEL); + if (!vendor_event) { + hdd_err("cfg80211_vendor_event_alloc failed"); + return -ENOMEM; + } + + hang_reason = hdd_convert_hang_reason(reason); + + if (nla_put_u32(vendor_event, QCA_WLAN_VENDOR_ATTR_HANG_REASON, + (uint32_t) hang_reason)) { + hdd_err("QCA_WLAN_VENDOR_ATTR_HANG_REASON put fail"); + kfree_skb(vendor_event); + return -EINVAL; + } + + cfg80211_vendor_event(vendor_event, GFP_KERNEL); + + EXIT(); + return 0; +} +#undef HANG_REASON_INDEX + /** * wlan_hdd_get_adjacent_chan(): Gets next/previous channel * with respect to the channel passed. @@ -1317,6 +1416,10 @@ static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] = .vendor_id = QCA_NL80211_VENDOR_ID, .subcmd = QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET, }, + [QCA_NL80211_VENDOR_SUBCMD_HANG_REASON_INDEX] = { + .vendor_id = QCA_NL80211_VENDOR_ID, + .subcmd = QCA_NL80211_VENDOR_SUBCMD_HANG, + }, #ifdef WLAN_UMAC_CONVERGENCE COMMON_VENDOR_EVENTS diff --git a/core/hdd/src/wlan_hdd_cfg80211.h b/core/hdd/src/wlan_hdd_cfg80211.h index 83873793196d..bd3b5c606abe 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.h +++ b/core/hdd/src/wlan_hdd_cfg80211.h @@ -326,6 +326,15 @@ void wlan_hdd_testmode_rx_event(void *buf, size_t buf_len); int wlan_hdd_send_avoid_freq_event(struct hdd_context *hdd_ctx, struct ch_avoid_ind_type *avoid_freq_list); +/** + * wlan_hdd_send_hang_reason_event() - Send hang reason to the userspace + * @hdd_ctx: Pointer to hdd context + * @reason: cds recovery reason + * + * Return: 0 on success or failure reason + */ +int wlan_hdd_send_hang_reason_event(struct hdd_context *pHddCtx, uint32_t reason); + int wlan_hdd_send_avoid_freq_for_dnbs(struct hdd_context *hdd_ctx, uint8_t op_chan); #ifdef FEATURE_WLAN_EXTSCAN diff --git a/core/hdd/src/wlan_hdd_driver_ops.c b/core/hdd/src/wlan_hdd_driver_ops.c index 55d6ced4ee15..416d30e7807a 100644 --- a/core/hdd/src/wlan_hdd_driver_ops.c +++ b/core/hdd/src/wlan_hdd_driver_ops.c @@ -473,6 +473,24 @@ static inline void hdd_wlan_ssr_shutdown_event(void) }; #endif +/** + * hdd_send_hang_reason() - Send hang reason to the userspace + * + * Return: None + */ +static void hdd_send_hang_reason(void) +{ + uint32_t reason = 0; + struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD); + + if (wlan_hdd_validate_context(hdd_ctx)) + return; + + cds_get_recovery_reason(&reason); + cds_reset_recovery_reason(); + wlan_hdd_send_hang_reason_event(hdd_ctx, reason); +} + /** * wlan_hdd_shutdown() - wlan_hdd_shutdown * @@ -505,6 +523,7 @@ static void wlan_hdd_shutdown(void) /* this is for cases, where shutdown invoked from platform */ cds_set_recovery_in_progress(true); hdd_wlan_ssr_shutdown_event(); + hdd_send_hang_reason(); if (!cds_wait_for_external_threads_completion(__func__)) hdd_err("Host is not ready for SSR, attempting anyway"); diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c index 1ea35e90a36a..87b987f2e8f1 100644 --- a/core/hdd/src/wlan_hdd_hostapd.c +++ b/core/hdd/src/wlan_hdd_hostapd.c @@ -2929,7 +2929,7 @@ static int __iw_softap_set_two_ints_getnone(struct net_device *dev, return 0; } if (value[1] == 3) { - cds_trigger_recovery(); + cds_trigger_recovery(QDF_REASON_UNSPECIFIED); return 0; } ret = wma_cli_set2_command(adapter->sessionId, diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c index 582a2cf08d0e..aa4a23df1611 100644 --- a/core/hdd/src/wlan_hdd_main.c +++ b/core/hdd/src/wlan_hdd_main.c @@ -12686,7 +12686,7 @@ void hdd_drv_ops_inactivity_handler(void) QDF_BUG(0); if (cds_is_self_recovery_enabled()) - cds_trigger_recovery(); + cds_trigger_recovery(QDF_REASON_UNSPECIFIED); else QDF_BUG(0); } diff --git a/core/hdd/src/wlan_hdd_scan.c b/core/hdd/src/wlan_hdd_scan.c index b0a2b4965695..4ab0e8d7294e 100644 --- a/core/hdd/src/wlan_hdd_scan.c +++ b/core/hdd/src/wlan_hdd_scan.c @@ -419,6 +419,9 @@ static int wlan_hdd_update_scan_ies(struct hdd_adapter *adapter, return 0; } +/* Define short name to use in cds_trigger_recovery */ +#define SCAN_FAILURE QDF_SCAN_ATTEMPT_FAILURES + /** * __wlan_hdd_cfg80211_scan() - API to process cfg80211 scan request * @wiphy: Pointer to wiphy @@ -570,7 +573,7 @@ static int __wlan_hdd_cfg80211_scan(struct wiphy *wiphy, hdd_ctx->config->enableSelfRecovery); } else { hdd_err("Triggering SSR due to scan stuck"); - cds_trigger_recovery(); + cds_trigger_recovery(SCAN_FAILURE); } } } @@ -880,6 +883,7 @@ free_mem: #endif } +#undef SCAN_FAILURE /** * wlan_hdd_cfg80211_scan() - API to process cfg80211 scan request diff --git a/core/hdd/src/wlan_hdd_wext.c b/core/hdd/src/wlan_hdd_wext.c index 476c68b6d8a9..e3f098c9806a 100644 --- a/core/hdd/src/wlan_hdd_wext.c +++ b/core/hdd/src/wlan_hdd_wext.c @@ -13065,7 +13065,7 @@ static int __iw_set_two_ints_getnone(struct net_device *dev, return 0; } if (value[1] == 3) { - cds_trigger_recovery(); + cds_trigger_recovery(QDF_REASON_UNSPECIFIED); return 0; } ret = wma_cli_set2_command(adapter->sessionId, diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c index 69a1a36fafdc..517786447ae7 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -462,7 +462,7 @@ tSmeCmd *sme_get_command_buffer(tpAniSirGlobal pMac) false, pMac->sme.enableSelfRecovery ? true : false); else - cds_trigger_recovery(); + cds_trigger_recovery(QDF_GET_MSG_BUFF_FAILURE); } /* memset to zero */ diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c index b14019fc05e3..e35b98c76aed 100644 --- a/core/wma/src/wma_features.c +++ b/core/wma/src/wma_features.c @@ -3928,7 +3928,7 @@ static inline void wma_suspend_target_timeout(bool is_self_recovery_enabled) WMA_LOGE("%s: Module (un)loading; Ignoring suspend timeout", __func__); else - cds_trigger_recovery(); + cds_trigger_recovery(QDF_SUSPEND_TIMEOUT); } #ifdef FEATURE_WLAN_TDLS