Parcourir la source

qcacld-3.0: Fix peer ref count deletion during peer detach

Data path may be processing received packets while the peer is being
deleted. It may create an extra reference to peer. As a protection,
decrement peer->ref_cnt by the exact number of peer_id_ref_cnt
instead of resetting it to hardcoded value of 1. Peer object will be
freed only after the last reference count is decremented.

CRs-Fixed: 2023550
Change-Id: I457a0fdd9d0bea86d22139090afbd33bdc34f2ed
Deepak Dhamdhere il y a 8 ans
Parent
commit
d40f4b1b62
2 fichiers modifiés avec 15 ajouts et 15 suppressions
  1. 3 14
      core/dp/txrx/ol_txrx.c
  2. 12 1
      core/dp/txrx/ol_txrx_peer_find.c

+ 3 - 14
core/dp/txrx/ol_txrx.c

@@ -3525,10 +3525,10 @@ static void ol_txrx_peer_detach(void *ppeer)
  * ol_txrx_peer_detach_force_delete() - Detach and delete a peer's data object
  * @peer - the object to detach
  *
- * Detach a peer and force the peer object to be removed. It is called during
+ * Detach a peer and force peer object to be removed. It is called during
  * roaming scenario when the firmware has already deleted a peer.
- * Peer object is freed immediately to avoid duplicate peers during roam sync
- * indication processing.
+ * Remove it from the peer_id_to_object map. Peer object is actually freed
+ * when last reference is deleted.
  *
  * Return: None
  */
@@ -3541,18 +3541,7 @@ static void ol_txrx_peer_detach_force_delete(void *ppeer)
 		__func__, peer, qdf_atomic_read(&peer->ref_cnt));
 
 	/* Clear the peer_id_to_obj map entries */
-	qdf_spin_lock_bh(&pdev->peer_map_unmap_lock);
 	ol_txrx_peer_remove_obj_map_entries(pdev, peer);
-	qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
-
-	/*
-	 * Set ref_cnt = 1 so that OL_TXRX_PEER_UNREF_DELETE() called by
-	 * ol_txrx_peer_detach() will actually delete this peer entry properly.
-	 */
-	qdf_spin_lock_bh(&pdev->peer_ref_mutex);
-	qdf_atomic_set(&peer->ref_cnt, 1);
-	qdf_spin_unlock_bh(&pdev->peer_ref_mutex);
-
 	ol_txrx_peer_detach(peer);
 }
 

+ 12 - 1
core/dp/txrx/ol_txrx_peer_find.c

@@ -606,7 +606,11 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
 /**
  * ol_txrx_peer_remove_obj_map_entries() - Remove matching pdev peer map entries
  * @pdev: pdev handle
- * @peer: peer to be removed
+ * @peer: peer for removing obj map entries
+ *
+ * Saves peer_id_ref_cnt to a different field and removes the link
+ * to peer object. It also decrements the peer reference count by
+ * the number of references removed.
  *
  * Return: None
  */
@@ -616,7 +620,9 @@ void ol_txrx_peer_remove_obj_map_entries(ol_txrx_pdev_handle pdev,
 	int i;
 	uint16_t peer_id;
 	int32_t peer_id_ref_cnt;
+	int32_t num_deleted_maps = 0;
 
+	qdf_spin_lock_bh(&pdev->peer_map_unmap_lock);
 	for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) {
 		peer_id = peer->peer_ids[i];
 		if (peer_id == HTT_INVALID_PEER ||
@@ -649,9 +655,14 @@ void ol_txrx_peer_remove_obj_map_entries(ol_txrx_pdev_handle pdev,
 			&pdev->peer_id_to_obj_map[peer_id].del_peer_id_ref_cnt);
 		qdf_atomic_init(&pdev->peer_id_to_obj_map[peer_id].
 				peer_id_ref_cnt);
+		num_deleted_maps += peer_id_ref_cnt;
 		pdev->peer_id_to_obj_map[peer_id].peer = NULL;
 		peer->peer_ids[i] = HTT_INVALID_PEER;
 	}
+	qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
+
+	while (num_deleted_maps-- > 0)
+		OL_TXRX_PEER_UNREF_DELETE(peer);
 }
 
 struct ol_txrx_peer_t *ol_txrx_assoc_peer_find(struct ol_txrx_vdev_t *vdev)