Kaynağa Gözat

qcacmn: Fix dead lock in connection completion handler

Connection completion handler is called from two scenarios.
Scenario 1: Called via connection flush with lock held
cm_disconnect_complete()-->
cm_flush_pending_request() acquired lock here-->
cm_handle_connect_flush()-->
cm_notify_connect_complete()

Scenario 2: Called via connection response without lock held

In first scenario, cm_notify_connect_complete() calls
cm_is_connect_id_reassoc_in_non_connected() to check if connect
request is a reassoc req and received in not connected state.
This function tries to auqire same lock which leads to dead lock.

To fix this issue, check if cm_notify_connect_complete() is called
with lock held or not. If not, acquire lock and call
cm_is_connect_id_reassoc_in_non_connected().

This prevents dead lock in scenario 1 and allows calling
cm_is_connect_id_reassoc_in_non_connected() with lock held for
scenario 2.

CRs-Fixed: 3351119
Change-Id: I192090777cb2cf0d604e2670a317aaf6f320d086
Shreedhar Parande 2 yıl önce
ebeveyn
işleme
08c3b4d0b8

+ 17 - 12
umac/mlme/connection_mgr/core/src/wlan_cm_connect.c

@@ -2347,7 +2347,8 @@ cm_clear_vdev_mlo_cap(struct wlan_objmgr_vdev *vdev)
  * @cm_ctx: connection manager context
  * @cm_id: cm id
  *
- * If connect req is a reassoc req and received in not connected state
+ * If connect req is a reassoc req and received in not connected state.
+ * Caller should take cm_ctx lock.
  *
  * Return: bool
  */
@@ -2362,7 +2363,6 @@ static bool cm_is_connect_id_reassoc_in_non_connected(struct cnx_mgr *cm_ctx,
 	if (prefix != CONNECT_REQ_PREFIX)
 		return is_reassoc;
 
-	cm_req_lock_acquire(cm_ctx);
 	qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
 	while (cur_node) {
 		qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
@@ -2371,20 +2371,19 @@ static bool cm_is_connect_id_reassoc_in_non_connected(struct cnx_mgr *cm_ctx,
 		if (cm_req->cm_id == cm_id) {
 			if (cm_req->connect_req.req.reassoc_in_non_connected)
 				is_reassoc = true;
-			cm_req_lock_release(cm_ctx);
 			return is_reassoc;
 		}
 
 		cur_node = next_node;
 		next_node = NULL;
 	}
-	cm_req_lock_release(cm_ctx);
 
 	return is_reassoc;
 }
 
 QDF_STATUS cm_notify_connect_complete(struct cnx_mgr *cm_ctx,
-				      struct wlan_cm_connect_resp *resp)
+				      struct wlan_cm_connect_resp *resp,
+				      bool acquire_lock)
 {
 	enum wlan_cm_sm_state sm_state;
 
@@ -2398,12 +2397,18 @@ QDF_STATUS cm_notify_connect_complete(struct cnx_mgr *cm_ctx,
 	 * kernel flags
 	 */
 	if (QDF_IS_STATUS_ERROR(resp->connect_status) &&
-	    sm_state == WLAN_CM_S_INIT &&
-	    cm_is_connect_id_reassoc_in_non_connected(cm_ctx, resp->cm_id)) {
-		resp->send_disconnect = true;
-		mlme_debug(CM_PREFIX_FMT "Set send disconnect to true to indicate disconnect instead of connect resp",
-			   CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
-					 resp->cm_id));
+	    sm_state == WLAN_CM_S_INIT) {
+		if (acquire_lock)
+			cm_req_lock_acquire(cm_ctx);
+		if (cm_is_connect_id_reassoc_in_non_connected(cm_ctx,
+							      resp->cm_id)) {
+			resp->send_disconnect = true;
+			mlme_debug(CM_PREFIX_FMT "Set send disconnect to true to indicate disconnect instead of connect resp",
+				   CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
+						 resp->cm_id));
+		}
+		if (acquire_lock)
+			cm_req_lock_release(cm_ctx);
 	}
 	mlme_cm_osif_connect_complete(cm_ctx->vdev, resp);
 	cm_if_mgr_inform_connect_complete(cm_ctx->vdev,
@@ -2446,7 +2451,7 @@ QDF_STATUS cm_connect_complete(struct cnx_mgr *cm_ctx,
 		send_ind = false;
 
 	if (send_ind)
-		cm_notify_connect_complete(cm_ctx, resp);
+		cm_notify_connect_complete(cm_ctx, resp, 1);
 
 	/* Update scan entry in case connect is success or fails with bssid */
 	if (!qdf_is_macaddr_zero(&resp->bssid)) {

+ 4 - 1
umac/mlme/connection_mgr/core/src/wlan_cm_main_api.h

@@ -226,13 +226,16 @@ QDF_STATUS cm_connect_rsp(struct wlan_objmgr_vdev *vdev,
  * connect response notification
  * @cm_ctx: connection manager context
  * @resp: connection complete resp.
+ * @acquire_lock: Flag to indicate whether this function needs
+ * cm_ctx lock or not.
  *
  * This API would be called after connection completion resp from VDEV mgr
  *
  * Return: QDF status
  */
 QDF_STATUS cm_notify_connect_complete(struct cnx_mgr *cm_ctx,
-				      struct wlan_cm_connect_resp *resp);
+				      struct wlan_cm_connect_resp *resp,
+				      bool acquire_lock);
 /**
  * cm_connect_complete() - This API would be called after connect complete
  * request from the serialization.

+ 1 - 1
umac/mlme/connection_mgr/core/src/wlan_cm_util.c

@@ -503,7 +503,7 @@ cm_handle_connect_flush(struct cnx_mgr *cm_ctx, struct cm_req *cm_req)
 	/* Get bssid and ssid and freq for the cm id from the req list */
 	cm_fill_connect_resp_from_req(resp, cm_req);
 
-	cm_notify_connect_complete(cm_ctx, resp);
+	cm_notify_connect_complete(cm_ctx, resp, 0);
 	qdf_mem_free(resp);
 }