Forráskód Böngészése

qcacmn: Fix deadlock in dp_delete_pending_vdev() API

In Data-path, dp_delete_pending_vdev() acquires
pdev->vdev_list_lock in the beginning and calls
dp_tx_flow_pool_unmap_handler() API which further try to acquires
same vdev_list_lock. This situation causes deadlock.

To fix the situation, re-factor dp_delete_pending_vdev() API.

CRs-Fixed: 2320629
Change-Id: Ie565ea07f396d1987fbe45173b49b2f50bb76cea
Krunal Soni 6 éve
szülő
commit
a5211f26a2
1 módosított fájl, 39 hozzáadás és 28 törlés
  1. 39 28
      dp/wifi3.0/dp_main.c

+ 39 - 28
dp/wifi3.0/dp_main.c

@@ -4648,40 +4648,44 @@ static void dp_reset_and_release_peer_mem(struct dp_soc *soc,
 	qdf_mem_free(peer);
 }
 
-static void dp_delete_pending_vdev(struct dp_pdev *pdev, uint32_t vdev_id)
+/**
+ * dp_delete_pending_vdev() - check and process vdev delete
+ * @pdev: DP specific pdev pointer
+ * @vdev: DP specific vdev pointer
+ * @vdev_id: vdev id corresponding to vdev
+ *
+ * This API does following:
+ * 1) It releases tx flow pools buffers as vdev is
+ *    going down and no peers are associated.
+ * 2) It also detaches vdev before cleaning vdev (struct dp_vdev) memory
+ */
+static void dp_delete_pending_vdev(struct dp_pdev *pdev, struct dp_vdev *vdev,
+				   uint8_t vdev_id)
 {
-	struct dp_vdev *vdev = NULL;
 	ol_txrx_vdev_delete_cb vdev_delete_cb = NULL;
 	void *vdev_delete_context = NULL;
 
-	qdf_spin_lock_bh(&pdev->vdev_list_lock);
-	TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) {
-		if (vdev->vdev_id == vdev_id)
-			break;
-	}
-	if (!vdev) {
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-			  "vdev is NULL");
-	} else if (vdev->delete.pending) {
-		vdev_delete_cb = vdev->delete.callback;
-		vdev_delete_context = vdev->delete.context;
+	vdev_delete_cb = vdev->delete.callback;
+	vdev_delete_context = vdev->delete.context;
 
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH,
-			  FL("deleting vdev object %pK (%pM)- its last peer is done"),
-			  vdev, vdev->mac_addr.raw);
-		/* all peers are gone, go ahead and delete it */
-		dp_tx_flow_pool_unmap_handler(pdev, vdev_id,
-				FLOW_TYPE_VDEV, vdev_id);
-		dp_tx_vdev_detach(vdev);
-		TAILQ_REMOVE(&pdev->vdev_list, vdev, vdev_list_elem);
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH,
-			  FL("deleting vdev object %pK (%pM)"),
-			  vdev, vdev->mac_addr.raw);
-		qdf_mem_free(vdev);
-		vdev = NULL;
-	}
+	QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH,
+		  FL("deleting vdev object %pK (%pM)- its last peer is done"),
+		  vdev, vdev->mac_addr.raw);
+	/* all peers are gone, go ahead and delete it */
+	dp_tx_flow_pool_unmap_handler(pdev, vdev_id,
+			FLOW_TYPE_VDEV, vdev_id);
+	dp_tx_vdev_detach(vdev);
+
+	qdf_spin_lock_bh(&pdev->vdev_list_lock);
+	TAILQ_REMOVE(&pdev->vdev_list, vdev, vdev_list_elem);
 	qdf_spin_unlock_bh(&pdev->vdev_list_lock);
 
+	QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH,
+		  FL("deleting vdev object %pK (%pM)"),
+		  vdev, vdev->mac_addr.raw);
+	qdf_mem_free(vdev);
+	vdev = NULL;
+
 	if (vdev_delete_cb)
 		vdev_delete_cb(vdev_delete_context);
 }
@@ -4701,6 +4705,7 @@ void dp_peer_unref_delete(void *peer_handle)
 	int found = 0;
 	uint16_t peer_id;
 	uint16_t vdev_id;
+	bool delete_vdev;
 
 	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH,
 		  "%s: peer %pK ref_cnt(before decrement): %d\n", __func__,
@@ -4762,6 +4767,11 @@ void dp_peer_unref_delete(void *peer_handle)
 
 		/* check whether the parent vdev has no peers left */
 		if (TAILQ_EMPTY(&vdev->peer_list)) {
+			/*
+			 * capture vdev delete pending flag's status
+			 * while holding peer_ref_mutex lock
+			 */
+			delete_vdev = vdev->delete.pending;
 			/*
 			 * Now that there are no references to the peer, we can
 			 * release the peer reference lock.
@@ -4771,7 +4781,8 @@ void dp_peer_unref_delete(void *peer_handle)
 			 * Check if the parent vdev was waiting for its peers
 			 * to be deleted, in order for it to be deleted too.
 			 */
-			dp_delete_pending_vdev(pdev, vdev_id);
+			if (delete_vdev)
+				dp_delete_pending_vdev(pdev, vdev, vdev_id);
 		} else {
 			qdf_spin_unlock_bh(&soc->peer_ref_mutex);
 		}