瀏覽代碼

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
Deeksha Gupta 2 年之前
父節點
當前提交
1d58140b54

+ 3 - 3
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);

+ 5 - 0
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_ */

+ 8 - 0
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);
+}

+ 30 - 0
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

+ 50 - 0
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;
+}

+ 3 - 2
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: