Эх сурвалжийг харах

qcacmn: Avoid deadlock for SM lock and MLO lock

When disconnect/connect from os_if and target_if happen at same time, wpa
supplicant holds CM lock and tries to get MLO lock, while scheduler thread
holds MLO lock and tries to get CM lock, dead lock will happen.

To avoid it, when MLO connect or disconnect, before get CM lock, release
MLO lock, get ref count for each vdev to avoid vdev deleted.

Change-Id: I2007c5b446c67ef142265c6ae0bba87a1980a55c
CRs-Fixed: 3313674
Jianmin Zhu 2 жил өмнө
parent
commit
79c7463a3b

+ 59 - 46
umac/mlo_mgr/src/wlan_mlo_mgr_sta.c

@@ -146,7 +146,7 @@ bool ucfg_mlo_is_mld_disconnected(struct wlan_objmgr_vdev *vdev)
 /**
  * mlo_send_link_disconnect- Issue the disconnect request on MLD links
  *
- * @mlo_dev_ctx: pointer to mlo dev context
+ * @vdev: pointer to vdev
  * @source: disconnect source
  * @reason_code: disconnect reason
  * @bssid: bssid of AP to disconnect, can be null if not known
@@ -154,7 +154,7 @@ bool ucfg_mlo_is_mld_disconnected(struct wlan_objmgr_vdev *vdev)
  * Return: QDF_STATUS
  */
 static QDF_STATUS
-mlo_send_link_disconnect(struct wlan_mlo_dev_context *mlo_dev_ctx,
+mlo_send_link_disconnect(struct wlan_objmgr_vdev *vdev,
 			 enum wlan_cm_source source,
 			 enum wlan_reason_code reason_code,
 			 struct qdf_mac_addr *bssid)
@@ -162,7 +162,16 @@ mlo_send_link_disconnect(struct wlan_mlo_dev_context *mlo_dev_ctx,
 	uint8_t i = 0;
 	enum wlan_cm_source link_source = source;
 	struct wlan_objmgr_vdev *assoc_vdev =
-			mlo_get_assoc_link_vdev(mlo_dev_ctx);
+			mlo_get_assoc_link_vdev(vdev->mlo_dev_ctx);
+	struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS];
+	uint16_t vdev_count = 0;
+	struct wlan_mlo_sta *sta_ctx = NULL;
+
+	sta_ctx = vdev->mlo_dev_ctx->sta_ctx;
+	if (!sta_ctx) {
+		mlo_err("Invalid sta_ctx");
+		return QDF_STATUS_E_FAILURE;
+	}
 
 	if (!assoc_vdev)
 		return QDF_STATUS_E_FAILURE;
@@ -174,19 +183,20 @@ mlo_send_link_disconnect(struct wlan_mlo_dev_context *mlo_dev_ctx,
 	if (source != CM_OSIF_DISCONNECT)
 		link_source = CM_MLO_LINK_VDEV_DISCONNECT;
 
-	for (i =  0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) {
-		if (!mlo_dev_ctx->wlan_vdev_list[i])
-			continue;
-
-		if (qdf_test_bit(i, mlo_dev_ctx->sta_ctx->wlan_connected_links) &&
-		    mlo_dev_ctx->wlan_vdev_list[i] != mlo_get_assoc_link_vdev(mlo_dev_ctx))
-			wlan_cm_disconnect(mlo_dev_ctx->wlan_vdev_list[i],
+	mlo_sta_get_vdev_list(vdev, &vdev_count, wlan_vdev_list);
+	for (i =  0; i < vdev_count; i++) {
+		if (qdf_test_bit(i, sta_ctx->wlan_connected_links) &&
+		    wlan_vdev_list[i] !=
+		    mlo_get_assoc_link_vdev(vdev->mlo_dev_ctx))
+			wlan_cm_disconnect(wlan_vdev_list[i],
 					   link_source, reason_code,
 					   NULL);
+		mlo_release_vdev_ref(wlan_vdev_list[i]);
 	}
 
 	wlan_cm_disconnect(assoc_vdev,
 			   source, reason_code, NULL);
+
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -407,7 +417,7 @@ static QDF_STATUS mlo_disconnect_no_lock(struct wlan_objmgr_vdev *vdev,
 			return status;
 		}
 
-		status = mlo_send_link_disconnect(mlo_dev_ctx, source,
+		status = mlo_send_link_disconnect(vdev, source,
 						  reason_code, bssid);
 	}
 
@@ -502,15 +512,19 @@ QDF_STATUS mlo_connect(struct wlan_objmgr_vdev *vdev,
 			mlo_err("Failed to allocate orig connect req");
 			copied_conn_req_lock_release(sta_ctx);
 			mlo_dev_lock_release(mlo_dev_ctx);
+
 			return QDF_STATUS_E_NOMEM;
 		}
 
 		if (QDF_IS_STATUS_SUCCESS(status)) {
 			mlo_clear_connected_links_bmap(vdev);
-			status = wlan_cm_start_connect(vdev, req);
+			mlo_dev_lock_release(mlo_dev_ctx);
+
+			return wlan_cm_start_connect(vdev, req);
 		}
 
 		mlo_dev_lock_release(mlo_dev_ctx);
+
 		return status;
 	}
 
@@ -610,6 +624,8 @@ mlo_send_link_connect(struct wlan_objmgr_vdev *vdev,
 	/* Create the secondary interface, Send keys if the last link */
 	uint8_t i, partner_idx = 0;
 	struct wlan_ssid ssid = {0};
+	struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS];
+	uint16_t vdev_count = 0;
 
 	mlo_debug("Sending link connect on partner interface");
 	wlan_vdev_mlme_get_ssid(
@@ -624,24 +640,27 @@ mlo_send_link_connect(struct wlan_objmgr_vdev *vdev,
 	if(wlan_vdev_mlme_is_mlo_link_vdev(vdev))
 		return;
 
-	for (i =  0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) {
-		if (!mlo_dev_ctx->wlan_vdev_list[i] ||
-		    (mlo_dev_ctx->wlan_vdev_list[i] == vdev))
+	mlo_sta_get_vdev_list(vdev, &vdev_count, wlan_vdev_list);
+	for (i = 0; i < vdev_count; i++) {
+		if (wlan_vdev_list[i] == vdev) {
+			mlo_release_vdev_ref(wlan_vdev_list[i]);
 			continue;
+		}
 		wlan_vdev_mlme_set_mlo_vdev(mlo_dev_ctx->wlan_vdev_list[i]);
 		wlan_vdev_mlme_set_mlo_link_vdev(mlo_dev_ctx->wlan_vdev_list[i]);
 		wlan_vdev_set_link_id(
-		      mlo_dev_ctx->wlan_vdev_list[i],
+		      wlan_vdev_list[i],
 		      ml_parnter_info->partner_link_info[partner_idx].link_id);
 		ml_parnter_info->partner_link_info[partner_idx].vdev_id =
 			       wlan_vdev_get_id(mlo_dev_ctx->wlan_vdev_list[i]);
 		mlo_prepare_and_send_connect(
-				mlo_dev_ctx->wlan_vdev_list[i],
+				wlan_vdev_list[i],
 				*ml_parnter_info,
 				ml_parnter_info->partner_link_info[partner_idx],
 				ssid);
-		mlo_update_connected_links(mlo_dev_ctx->wlan_vdev_list[i], 1);
+		mlo_update_connected_links(wlan_vdev_list[i], 1);
 		partner_idx++;
+		mlo_release_vdev_ref(wlan_vdev_list[i]);
 	}
 }
 #else
@@ -1063,10 +1082,10 @@ QDF_STATUS mlo_disconnect(struct wlan_objmgr_vdev *vdev,
 			mlo_dev_lock_release(mlo_dev_ctx);
 			return status;
 		}
+		mlo_dev_lock_release(mlo_dev_ctx);
 
-		status = mlo_send_link_disconnect(mlo_dev_ctx, source,
+		status = mlo_send_link_disconnect(vdev, source,
 						  reason_code, bssid);
-		mlo_dev_lock_release(mlo_dev_ctx);
 		if (QDF_IS_STATUS_SUCCESS(status))
 			mlo_free_copied_conn_req(sta_ctx);
 
@@ -1120,44 +1139,41 @@ QDF_STATUS mlo_sync_disconnect(struct wlan_objmgr_vdev *vdev,
 /**
  * mlo_handle_disconnect_resp- Issue desired actions on partner link vdev
  *
- * @mlo_dev_ctx: pointer to mlo dev context
+ * @vdev: pointer to vdev
  * @resp: disconnect resp
  *
  * Return: none
  */
 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
 static
-void mlo_handle_disconnect_resp(struct wlan_mlo_dev_context *mlo_dev_ctx,
+void mlo_handle_disconnect_resp(struct wlan_objmgr_vdev *vdev,
 				struct wlan_cm_discon_rsp *resp)
 {
 /* If it is secondary link then delete vdev object from mlo device. */
 	enum wlan_cm_source source;
 	enum wlan_reason_code reason_code;
 	uint8_t i = 0;
+	struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS];
+	uint16_t vdev_count = 0;
 
-	mlo_dev_lock_acquire(mlo_dev_ctx);
-	for (i =  0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) {
-		if (!mlo_dev_ctx->wlan_vdev_list[i])
-			continue;
-
-		if (wlan_cm_is_vdev_connected(mlo_dev_ctx->wlan_vdev_list[i])) {
+	mlo_sta_get_vdev_list(vdev, &vdev_count, wlan_vdev_list);
+	for (i =  0; i < vdev_count; i++) {
+		if (wlan_cm_is_vdev_connected(wlan_vdev_list[i])) {
 			if (wlan_vdev_mlme_is_mlo_link_vdev(
-					mlo_dev_ctx->wlan_vdev_list[i])) {
+					wlan_vdev_list[i])) {
 				source = resp->req.req.source;
 				reason_code = resp->req.req.reason_code;
 				wlan_cm_disconnect(
-						mlo_dev_ctx->wlan_vdev_list[i],
+						wlan_vdev_list[i],
 						source, reason_code, NULL);
-				mlo_dev_lock_release(mlo_dev_ctx);
-				return;
 			}
 		}
+		mlo_release_vdev_ref(wlan_vdev_list[i]);
 	}
-	mlo_dev_lock_release(mlo_dev_ctx);
 }
 #else
 static
-void mlo_handle_disconnect_resp(struct wlan_mlo_dev_context *mlo_dev_ctx,
+void mlo_handle_disconnect_resp(struct wlan_objmgr_vdev *vdev,
 				struct wlan_cm_discon_rsp *resp)
 { }
 
@@ -1305,7 +1321,7 @@ void mlo_sta_link_disconn_notify(struct wlan_objmgr_vdev *vdev,
 		}
 	}
 
-	mlo_handle_disconnect_resp(mlo_dev_ctx, resp);
+	mlo_handle_disconnect_resp(vdev, resp);
 }
 
 bool mlo_is_mld_sta(struct wlan_objmgr_vdev *vdev)
@@ -1760,6 +1776,8 @@ void mlo_internal_disconnect_links(struct wlan_objmgr_vdev *vdev)
 	struct wlan_mlo_dev_context *mlo_dev_ctx = NULL;
 	struct wlan_mlo_sta *sta_ctx = NULL;
 	uint8_t i;
+	struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS];
+	uint16_t vdev_count = 0;
 
 	if (!vdev)
 		return;
@@ -1784,28 +1802,23 @@ void mlo_internal_disconnect_links(struct wlan_objmgr_vdev *vdev)
 		return;
 	}
 
-	mlo_dev_lock_acquire(mlo_dev_ctx);
 	if (sta_ctx->connect_req) {
 		mlo_free_connect_ies(sta_ctx->connect_req);
 		qdf_mem_free(sta_ctx->connect_req);
 		sta_ctx->connect_req = NULL;
 	}
 
-	for (i =  0; i < WLAN_UMAC_MLO_MAX_VDEVS; i++) {
-		if (!mlo_dev_ctx->wlan_vdev_list[i])
-			continue;
-
-		if (qdf_test_bit(i,
-				 mlo_dev_ctx->sta_ctx->wlan_connected_links) &&
-		    mlo_dev_ctx->wlan_vdev_list[i] !=
+	mlo_sta_get_vdev_list(vdev, &vdev_count, wlan_vdev_list);
+	for (i =  0; i < vdev_count; i++) {
+		if (qdf_test_bit(i, sta_ctx->wlan_connected_links) &&
+		    wlan_vdev_list[i] !=
 		    mlo_get_assoc_link_vdev(mlo_dev_ctx))
-			wlan_cm_disconnect(mlo_dev_ctx->wlan_vdev_list[i],
+			wlan_cm_disconnect(wlan_vdev_list[i],
 					   CM_INTERNAL_DISCONNECT,
 					   REASON_UNSPEC_FAILURE,
 					   NULL);
+		mlo_release_vdev_ref(wlan_vdev_list[i]);
 	}
-
-	mlo_dev_lock_release(mlo_dev_ctx);
 }
 
 void mlo_sta_get_vdev_list(struct wlan_objmgr_vdev *vdev, uint16_t *vdev_count,