From 6001ff7264102fce92f0316975e22bff139b986d Mon Sep 17 00:00:00 2001 From: Yu Tian Date: Sat, 15 Aug 2020 19:12:44 +0800 Subject: [PATCH] qcacld-3.0: Add RX frame pending check for WoW Sometimes frames are queued to dp_rx_thread wait queue during WoW suspend in progress. At this time dp_rx_thread is in SUSPEND state and these frames can't deliver to Linux Stack, for ICMP case, if no other frames come to wake up system, ICMP timeout will happen. Add a RX suspend check in WoW to avoid suspend when frame comes before WMI_WOW_ENABLE sends to FW Change-Id: I3d37e6d6ce8f7f6edaf5f78bf5cef77bee6ed5c6 CRs-Fixed: 2755583 --- .../pmo/core/src/wlan_pmo_suspend_resume.c | 8 ++++ core/cds/src/cds_api.c | 1 + core/dp/txrx3.0/dp_txrx.c | 46 ++++++++++++++++++- core/dp/txrx3.0/dp_txrx.h | 9 ++++ core/hdd/src/wlan_hdd_driver_ops.c | 12 +++++ 5 files changed, 75 insertions(+), 1 deletion(-) diff --git a/components/pmo/core/src/wlan_pmo_suspend_resume.c b/components/pmo/core/src/wlan_pmo_suspend_resume.c index 95dbbee02b3a..e9d6d5020e86 100644 --- a/components/pmo/core/src/wlan_pmo_suspend_resume.c +++ b/components/pmo/core/src/wlan_pmo_suspend_resume.c @@ -961,6 +961,7 @@ QDF_STATUS pmo_core_psoc_bus_runtime_suspend(struct wlan_objmgr_psoc *psoc, int ret; struct pmo_wow_enable_params wow_params = {0}; qdf_time_t begin, end; + int pending; pmo_enter(); @@ -1026,6 +1027,13 @@ QDF_STATUS pmo_core_psoc_bus_runtime_suspend(struct wlan_objmgr_psoc *psoc, goto pmo_bus_resume; } + pending = cdp_rx_get_pending(cds_get_context(QDF_MODULE_ID_SOC)); + if (pending) { + pmo_debug("Prevent suspend, RX frame pending %d", pending); + status = QDF_STATUS_E_BUSY; + goto resume_hif; + } + if (pld_cb) { begin = qdf_get_log_timestamp_usecs(); ret = pld_cb(); diff --git a/core/cds/src/cds_api.c b/core/cds/src/cds_api.c index 0ae4678c41e1..210a2b41e7fa 100644 --- a/core/cds/src/cds_api.c +++ b/core/cds/src/cds_api.c @@ -121,6 +121,7 @@ static struct ol_if_ops dp_ol_if_ops = { .is_roam_inprogress = wma_is_roam_in_progress, .get_con_mode = cds_get_conparam, .send_delba = cds_send_delba, + .dp_rx_get_pending = dp_rx_tm_get_pending, /* TODO: Add any other control path calls required to OL_IF/WMA layer */ }; #else diff --git a/core/dp/txrx3.0/dp_txrx.c b/core/dp/txrx3.0/dp_txrx.c index 7e126f395f5a..41c52a1eec38 100644 --- a/core/dp/txrx3.0/dp_txrx.c +++ b/core/dp/txrx3.0/dp_txrx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -87,3 +87,47 @@ QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc) return QDF_STATUS_SUCCESS; } + +/** + * dp_rx_tm_get_pending() - get number of frame in thread + * nbuf queue pending + * @soc: ol_txrx_soc_handle object + * + * Return: number of frames + */ +#ifdef FEATURE_WLAN_DP_RX_THREADS +int dp_rx_tm_get_pending(ol_txrx_soc_handle soc) +{ + int i; + int num_pending = 0; + struct dp_rx_thread *rx_thread; + struct dp_txrx_handle *dp_ext_hdl; + struct dp_rx_tm_handle *rx_tm_hdl; + + if (!soc) + return 0; + + dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc); + if (!dp_ext_hdl) + return 0; + + rx_tm_hdl = &dp_ext_hdl->rx_tm_hdl; + + for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) { + rx_thread = rx_tm_hdl->rx_thread[i]; + if (!rx_thread) + continue; + num_pending += qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue); + } + + if (num_pending) + dp_debug("pending frames in thread queue %d", num_pending); + + return num_pending; +} +#else +int dp_rx_tm_get_pending(ol_txrx_soc_handle soc) +{ + return 0; +} +#endif diff --git a/core/dp/txrx3.0/dp_txrx.h b/core/dp/txrx3.0/dp_txrx.h index 33c51d21a722..ba81ed44f2f1 100644 --- a/core/dp/txrx3.0/dp_txrx.h +++ b/core/dp/txrx3.0/dp_txrx.h @@ -434,4 +434,13 @@ QDF_STATUS dp_txrx_set_cpu_mask(ol_txrx_soc_handle soc, qdf_cpu_mask *new_mask) } #endif /* FEATURE_WLAN_DP_RX_THREADS */ + +/** + * dp_rx_tm_get_pending() - get number of frame in thread + * nbuf queue pending + * @soc: ol_txrx_soc_handle object + * + * Return: number of frames + */ +int dp_rx_tm_get_pending(ol_txrx_soc_handle soc); #endif /* _DP_TXRX_H */ diff --git a/core/hdd/src/wlan_hdd_driver_ops.c b/core/hdd/src/wlan_hdd_driver_ops.c index de66b3645bce..76ced7cd94bb 100644 --- a/core/hdd/src/wlan_hdd_driver_ops.c +++ b/core/hdd/src/wlan_hdd_driver_ops.c @@ -1037,6 +1037,7 @@ static int __wlan_hdd_bus_suspend(struct wow_enable_params wow_params) void *hif_ctx; void *dp_soc; struct pmo_wow_enable_params pmo_params; + int pending; hdd_info("starting bus suspend"); @@ -1098,6 +1099,13 @@ static int __wlan_hdd_bus_suspend(struct wow_enable_params wow_params) goto resume_pmo; } + pending = cdp_rx_get_pending(cds_get_context(QDF_MODULE_ID_SOC)); + if (pending) { + hdd_debug("Prevent suspend, RX frame pending %d", pending); + err = -EBUSY; + goto resume_hif; + } + /* * Remove bus votes at the very end, after making sure there are no * pending bus transactions from WLAN SOC for TX/RX. @@ -1107,6 +1115,10 @@ static int __wlan_hdd_bus_suspend(struct wow_enable_params wow_params) hdd_info("bus suspend succeeded"); return 0; +resume_hif: + status = hif_bus_resume(hif_ctx); + QDF_BUG(QDF_IS_STATUS_SUCCESS(status)); + resume_pmo: status = ucfg_pmo_psoc_bus_resume_req(hdd_ctx->psoc, QDF_SYSTEM_SUSPEND);