From 1d58140b545eb14c39ba95c9cf8265cf9855b19c Mon Sep 17 00:00:00 2001 From: Deeksha Gupta Date: Tue, 21 Jun 2022 15:44:48 +0530 Subject: [PATCH] qcacld-3.0: Avoid tacking CM lock after holding sme global lock When STA receives a connect req (C1) and it requires hw mode change. Now the hw mode change resp will take the SME global lock and call connection manager API, which will try to acquire CM lock. But if before that one more connect req (C2) is received in another thread which has acquired CM lock before C1 can acquire it, and this connect req C2 will also flush the C1 from CM queue. Now if C2 also require hw mode change, which also require to acquire SME global lock. So both connect will wait for each other to complete, leading to deadlock. To fix this, from hw mode change response, break the context (by posting to scheduler thread) so that sme global lock is released and C2 is processed. As C1 is already flushed by C2, on scheduler thread execution once it get CM lock it will be dropped silently, and C2 will proceed with connect. Change-Id: I14efb0f21442edcae90a4abea20cb0b9e06a0758 CRs-Fixed: 3223786 --- .../policy_mgr/src/wlan_policy_mgr_core.c | 6 +-- .../mlme/dispatcher/inc/wlan_mlme_api.h | 5 ++ .../mlme/dispatcher/src/wlan_mlme_api.c | 8 +++ .../core/src/wlan_cm_vdev_api.h | 30 +++++++++++ .../core/src/wlan_cm_vdev_connect.c | 50 +++++++++++++++++++ core/sme/src/common/sme_api.c | 5 +- 6 files changed, 99 insertions(+), 5 deletions(-) diff --git a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c index cf1332f982..f5e15d19f3 100644 --- a/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c +++ b/components/cmn_services/policy_mgr/src/wlan_policy_mgr_core.c @@ -4614,9 +4614,9 @@ static void policy_mgr_nss_update_cb(struct wlan_objmgr_psoc *psoc, reason == POLICY_MGR_UPDATE_REASON_LFR2_ROAM) { sme_debug("Continue connect/reassoc on vdev %d request_id %x reason %d", vdev_id, request_id, reason); - wlan_cm_hw_mode_change_resp(pm_ctx->pdev, vdev_id, - request_id, - QDF_STATUS_SUCCESS); + wlan_connect_hw_mode_change_resp(pm_ctx->pdev, vdev_id, + request_id, + QDF_STATUS_SUCCESS); } policy_mgr_debug("No action needed right now"); ret = policy_mgr_set_opportunistic_update(psoc); diff --git a/components/mlme/dispatcher/inc/wlan_mlme_api.h b/components/mlme/dispatcher/inc/wlan_mlme_api.h index 142a83dca9..061c1974f7 100644 --- a/components/mlme/dispatcher/inc/wlan_mlme_api.h +++ b/components/mlme/dispatcher/inc/wlan_mlme_api.h @@ -3775,4 +3775,9 @@ void wlan_mlme_get_safe_mode_enable(struct wlan_objmgr_psoc *psoc, * Return: 6g_power_type */ uint32_t wlan_mlme_get_6g_ap_power_type(struct wlan_objmgr_vdev *vdev); + +QDF_STATUS wlan_connect_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id, + wlan_cm_id cm_id, + QDF_STATUS status); #endif /* _WLAN_MLME_API_H_ */ diff --git a/components/mlme/dispatcher/src/wlan_mlme_api.c b/components/mlme/dispatcher/src/wlan_mlme_api.c index 28809ed6e6..b28608c900 100644 --- a/components/mlme/dispatcher/src/wlan_mlme_api.c +++ b/components/mlme/dispatcher/src/wlan_mlme_api.c @@ -31,6 +31,7 @@ #include "wlan_utility.h" #include "wlan_policy_mgr_ucfg.h" #include "wlan_vdev_mgr_utils_api.h" +#include <../../core/src/wlan_cm_vdev_api.h> /* quota in milliseconds */ #define MCC_DUTY_CYCLE 70 @@ -5944,3 +5945,10 @@ uint32_t wlan_mlme_get_6g_ap_power_type(struct wlan_objmgr_vdev *vdev) return mlme_obj->reg_tpc_obj.power_type_6g; } +QDF_STATUS wlan_connect_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id, + wlan_cm_id cm_id, QDF_STATUS status) +{ + return wlan_cm_handle_hw_mode_change_resp(pdev, vdev_id, cm_id, + status); +} diff --git a/components/umac/mlme/connection_mgr/core/src/wlan_cm_vdev_api.h b/components/umac/mlme/connection_mgr/core/src/wlan_cm_vdev_api.h index 75032b6310..48f2ccd769 100644 --- a/components/umac/mlme/connection_mgr/core/src/wlan_cm_vdev_api.h +++ b/components/umac/mlme/connection_mgr/core/src/wlan_cm_vdev_api.h @@ -107,6 +107,20 @@ struct cm_vdev_disconnect_rsp { struct wlan_objmgr_psoc *psoc; }; +/* + * struct cm_vdev_hw_mode_rsp - hw mode change from vdev mgr to CM + * @pdev: pdev pointer + * @vdev_id: vdev id + * @cm_id: connection ID which gave the hw mode change request + * @status: status of the HW mode change. + */ +struct cm_vdev_hw_mode_rsp { + struct wlan_objmgr_pdev *pdev; + uint8_t vdev_id; + wlan_cm_id cm_id; + QDF_STATUS status; +}; + /** * struct cm_vdev_join_rsp - connect rsp from vdev mgr to connection mgr * @psoc: psoc object @@ -691,6 +705,22 @@ QDF_STATUS wlan_cm_send_connect_rsp(struct scheduler_msg *msg); */ void wlan_cm_free_connect_rsp(struct cm_vdev_join_rsp *rsp); +/** + * wlan_cm_handle_hw_mode_change_resp() Process hw_mode_change_resp + * @pdev: pdev pointer + * @vdev_id: vdev_id + * @cm_id: connection manager id + * @status: status + * + * This API is to break the context and avoid calling CM API to take CM lock + * while holding SME lock. + * + * Return: QDF_STATUS + */ +QDF_STATUS +wlan_cm_handle_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id, + wlan_cm_id cm_id, QDF_STATUS status); /** * wlan_cm_rso_stop_continue_disconnect() - Continue disconnect after RSO stop * @psoc: psoc object diff --git a/components/umac/mlme/connection_mgr/core/src/wlan_cm_vdev_connect.c b/components/umac/mlme/connection_mgr/core/src/wlan_cm_vdev_connect.c index 83cfb257e8..7346061f16 100644 --- a/components/umac/mlme/connection_mgr/core/src/wlan_cm_vdev_connect.c +++ b/components/umac/mlme/connection_mgr/core/src/wlan_cm_vdev_connect.c @@ -1623,3 +1623,53 @@ bool cm_is_vdevid_active(struct wlan_objmgr_pdev *pdev, uint8_t vdev_id) return active; } + +static +QDF_STATUS cm_handle_hw_mode_change_resp_cb(struct scheduler_msg *msg) +{ + struct cm_vdev_hw_mode_rsp *rsp; + + if (!msg || !msg->bodyptr) + return QDF_STATUS_E_FAILURE; + + rsp = msg->bodyptr; + wlan_cm_hw_mode_change_resp(rsp->pdev, rsp->vdev_id, rsp->cm_id, + rsp->status); + + qdf_mem_free(rsp); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +wlan_cm_handle_hw_mode_change_resp(struct wlan_objmgr_pdev *pdev, + uint8_t vdev_id, + wlan_cm_id cm_id, QDF_STATUS status) +{ + struct cm_vdev_hw_mode_rsp *rsp; + struct scheduler_msg rsp_msg = {0}; + QDF_STATUS qdf_status; + + rsp = qdf_mem_malloc(sizeof(*rsp)); + if (!rsp) + return QDF_STATUS_E_FAILURE; + + rsp->pdev = pdev; + rsp->vdev_id = vdev_id; + rsp->cm_id = cm_id; + rsp->status = status; + + rsp_msg.bodyptr = rsp; + rsp_msg.callback = cm_handle_hw_mode_change_resp_cb; + + qdf_status = scheduler_post_message(QDF_MODULE_ID_MLME, + QDF_MODULE_ID_TARGET_IF, + QDF_MODULE_ID_TARGET_IF, &rsp_msg); + if (QDF_IS_STATUS_ERROR(qdf_status)) { + mlme_err(CM_PREFIX_FMT "Failed to post HW mode change rsp", + CM_PREFIX_REF(vdev_id, cm_id)); + qdf_mem_free(rsp); + } + + return qdf_status; +} diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c index 0ea76fa6d1..dfca5fc910 100644 --- a/core/sme/src/common/sme_api.c +++ b/core/sme/src/common/sme_api.c @@ -236,8 +236,9 @@ static QDF_STATUS sme_process_set_hw_mode_resp(struct mac_context *mac, uint8_t param->status == SET_HW_MODE_STATUS_ALREADY) status = QDF_STATUS_SUCCESS; - wlan_cm_hw_mode_change_resp(mac->pdev, session_id, request_id, - status); + wlan_cm_handle_hw_mode_change_resp(mac->pdev, session_id, + request_id, + status); } end: