From 4085ff7e51bdc154d6a61eb429b98d401d570aa0 Mon Sep 17 00:00:00 2001 From: Anurag Chouhan Date: Thu, 5 Oct 2017 18:09:56 +0530 Subject: [PATCH] qcacld-3.0: Add vendor event to get the driver hang reason Add Vendor Event to get the driver hang reason indicating to the user space that the driver has detected an internal failure. This event carries the information indicating the reason that triggered this detection. Change-Id: I3934f2a18c796ed3b53175dcbe7efd7f4d1409b9 CRs-fixed: 2098498 --- core/cds/inc/cds_api.h | 23 ++++++- core/cds/inc/cds_sched.h | 1 + core/cds/src/cds_api.c | 40 ++++++++++- core/dp/txrx/ol_txrx.c | 9 ++- core/hdd/src/wlan_hdd_cfg80211.c | 103 +++++++++++++++++++++++++++++ core/hdd/src/wlan_hdd_cfg80211.h | 9 +++ core/hdd/src/wlan_hdd_driver_ops.c | 19 ++++++ core/hdd/src/wlan_hdd_hostapd.c | 2 +- core/hdd/src/wlan_hdd_main.c | 2 +- core/hdd/src/wlan_hdd_scan.c | 6 +- core/hdd/src/wlan_hdd_wext.c | 2 +- core/sme/src/common/sme_api.c | 2 +- core/wma/src/wma_features.c | 2 +- 13 files changed, 210 insertions(+), 10 deletions(-) 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