Quellcode durchsuchen

qcacld-3.0: Fix race during peer deletion in roaming scenario

There is possiblity of peer being accesed in rx napi context,
when peer is getting freed up at same time from scheduler
thread context in roaming scenario.

To avoid this race free up the peer when unmap handlers received
from F.W for all the peer ids.

Change-Id: Ia0b1fcc6b56e91efe914ca431aeac6e83b773a81
CRs-Fixed: 2867382
Karthik Kantamneni vor 4 Jahren
Ursprung
Commit
153db6d0a5

+ 12 - 4
core/dp/txrx/ol_txrx.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -905,6 +905,8 @@ ol_txrx_pdev_attach(ol_txrx_soc_handle soc,
 
 	TAILQ_INIT(&pdev->vdev_list);
 
+	TAILQ_INIT(&pdev->inactive_peer_list);
+
 	TAILQ_INIT(&pdev->req_list);
 	pdev->req_list_depth = 0;
 	qdf_spinlock_create(&pdev->req_list_spinlock);
@@ -1714,6 +1716,7 @@ static void ol_txrx_pdev_pre_detach(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
 		ol_txrx_dbg("Force delete for pdev %pK\n",
 			   pdev);
 		ol_txrx_peer_find_hash_erase(pdev);
+		ol_txrx_peer_free_inactive_list(pdev);
 	}
 
 	/* to get flow pool status before freeing descs */
@@ -2515,6 +2518,7 @@ ol_txrx_peer_attach(struct cdp_soc_t *soc_hdl, uint8_t vdev_id,
 	qdf_atomic_init(&peer->delete_in_progress);
 	qdf_atomic_init(&peer->flush_in_progress);
 	qdf_atomic_init(&peer->ref_cnt);
+	qdf_atomic_init(&peer->del_ref_cnt);
 
 	for (i = 0; i < PEER_DEBUG_ID_MAX; i++)
 		qdf_atomic_init(&peer->access_list[i]);
@@ -3180,6 +3184,7 @@ int ol_txrx_peer_release_ref(ol_txrx_peer_handle peer,
 	bool ref_silent = true;
 	int access_list = 0;
 	uint32_t err_code = 0;
+	int del_rc;
 
 	/* preconditions */
 	TXRX_ASSERT2(peer);
@@ -3340,10 +3345,12 @@ int ol_txrx_peer_release_ref(ol_txrx_peer_handle peer,
 			qdf_spin_unlock_bh(&pdev->peer_ref_mutex);
 		}
 
-		ol_txrx_info_high("[%d][%d]: Deleting peer %pK ref_cnt -> %d %s",
+		del_rc = qdf_atomic_read(&peer->del_ref_cnt);
+
+		ol_txrx_info_high("[%d][%d]: Deleting peer %pK ref_cnt -> %d del_ref_cnt -> %d %s",
 				  debug_id,
 				  qdf_atomic_read(&peer->access_list[debug_id]),
-				  peer, rc,
+				  peer, rc, del_rc,
 				  qdf_atomic_read(&peer->fw_create_pending) ==
 				  1 ? "(No Maps received)" : "");
 
@@ -3363,7 +3370,8 @@ int ol_txrx_peer_release_ref(ol_txrx_peer_handle peer,
 		    pdev->self_peer == peer)
 			pdev->self_peer = NULL;
 
-		qdf_mem_free(peer);
+		if (!del_rc)
+			qdf_mem_free(peer);
 	} else {
 		access_list = qdf_atomic_read(&peer->access_list[debug_id]);
 		qdf_spin_unlock_bh(&pdev->peer_ref_mutex);

+ 38 - 5
core/dp/txrx/ol_txrx_peer_find.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -301,6 +301,21 @@ void ol_txrx_peer_find_hash_erase(struct ol_txrx_pdev_t *pdev)
 	}
 }
 
+void ol_txrx_peer_free_inactive_list(struct ol_txrx_pdev_t *pdev)
+{
+	struct ol_txrx_peer_t *peer = NULL, *tmp;
+
+	qdf_spin_lock_bh(&pdev->peer_map_unmap_lock);
+	if (!TAILQ_EMPTY(&pdev->inactive_peer_list)) {
+		TAILQ_FOREACH_SAFE(peer, &pdev->inactive_peer_list,
+				   inactive_peer_list_elem, tmp) {
+			qdf_atomic_init(&peer->del_ref_cnt); /* set to 0 */
+			qdf_mem_free(peer);
+		}
+	}
+	qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
+}
+
 /*=== function definitions for peer id --> peer object map ==================*/
 
 static int ol_txrx_peer_find_map_attach(struct ol_txrx_pdev_t *pdev)
@@ -618,6 +633,7 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
 	struct ol_txrx_peer_t *peer;
 	int i = 0;
 	int32_t ref_cnt;
+	int del_ref_cnt;
 
 	if (peer_id == HTT_INVALID_PEER) {
 		ol_txrx_err(
@@ -638,16 +654,28 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
 	if (qdf_atomic_read(
 		&pdev->peer_id_to_obj_map[peer_id].del_peer_id_ref_cnt)) {
 		/* This peer_id belongs to a peer already deleted */
-		qdf_atomic_dec(&pdev->peer_id_to_obj_map[peer_id].
-					del_peer_id_ref_cnt);
+		peer = pdev->peer_id_to_obj_map[peer_id].del_peer;
+		if (qdf_atomic_dec_and_test
+		    (&pdev->peer_id_to_obj_map[peer_id].del_peer_id_ref_cnt)) {
+			pdev->peer_id_to_obj_map[peer_id].del_peer = NULL;
+		}
+
+		del_ref_cnt = qdf_atomic_read(&peer->del_ref_cnt);
+		if (qdf_atomic_dec_and_test(&peer->del_ref_cnt)) {
+			TAILQ_REMOVE(&pdev->inactive_peer_list, peer,
+				     inactive_peer_list_elem);
+			qdf_mem_free(peer);
+		}
+		del_ref_cnt--;
+
 		ref_cnt = qdf_atomic_read(&pdev->peer_id_to_obj_map[peer_id].
 							del_peer_id_ref_cnt);
 		qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
 		wlan_roam_debug_log(DEBUG_INVALID_VDEV_ID,
 				    DEBUG_PEER_UNMAP_EVENT,
 				    peer_id, NULL, NULL, ref_cnt, 0x101);
-		ol_txrx_dbg("peer already deleted, peer_id %d del_peer_id_ref_cnt %d",
-			    peer_id, ref_cnt);
+		ol_txrx_dbg("peer already deleted, peer_id %d del_ref_cnt:%d del_peer_id_ref_cnt %d",
+			    peer_id, del_ref_cnt, ref_cnt);
 		return;
 	}
 	peer = pdev->peer_id_to_obj_map[peer_id].peer;
@@ -755,8 +783,13 @@ void ol_txrx_peer_remove_obj_map_entries(ol_txrx_pdev_handle pdev,
 				peer_id_ref_cnt);
 		num_deleted_maps += peer_id_ref_cnt;
 		pdev->peer_id_to_obj_map[peer_id].peer = NULL;
+		pdev->peer_id_to_obj_map[peer_id].del_peer = peer;
 		peer->peer_ids[i] = HTT_INVALID_PEER;
 	}
+	qdf_atomic_init(&peer->del_ref_cnt);
+	qdf_atomic_add(num_deleted_maps, &peer->del_ref_cnt);
+	TAILQ_INSERT_TAIL(&pdev->inactive_peer_list, peer,
+			  inactive_peer_list_elem);
 	qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
 
 	/* Debug print the information after releasing bh spinlock */

+ 3 - 1
core/dp/txrx/ol_txrx_peer_find.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011, 2015-2019,2021 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -115,6 +115,8 @@ ol_txrx_peer_find_hash_remove(struct ol_txrx_pdev_t *pdev,
 
 void ol_txrx_peer_find_hash_erase(struct ol_txrx_pdev_t *pdev);
 
+void ol_txrx_peer_free_inactive_list(struct ol_txrx_pdev_t *pdev);
+
 struct ol_txrx_peer_t *ol_txrx_assoc_peer_find(struct ol_txrx_vdev_t *vdev);
 void ol_txrx_peer_remove_obj_map_entries(ol_txrx_pdev_handle pdev,
 					struct ol_txrx_peer_t *peer);

+ 8 - 1
core/dp/txrx/ol_txrx_types.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -545,6 +545,7 @@ struct ol_tx_flow_pool_t {
  */
 struct ol_txrx_peer_id_map {
 	struct ol_txrx_peer_t *peer;
+	struct ol_txrx_peer_t *del_peer;
 	qdf_atomic_t peer_id_ref_cnt;
 	qdf_atomic_t del_peer_id_ref_cnt;
 	qdf_atomic_t peer_id_unmap_cnt;
@@ -726,6 +727,9 @@ struct ol_txrx_pdev_t {
 	/* ol_txrx_vdev list */
 	TAILQ_HEAD(, ol_txrx_vdev_t) vdev_list;
 
+	/* Inactive peer list */
+	TAILQ_HEAD(, ol_txrx_peer_t) inactive_peer_list;
+
 	TAILQ_HEAD(, ol_txrx_stats_req_internal) req_list;
 	int req_list_depth;
 	qdf_spinlock_t req_list_spinlock;
@@ -1430,6 +1434,7 @@ struct ol_txrx_peer_t {
 	struct cdp_ctrl_objmgr_peer *ctrl_peer;
 
 	qdf_atomic_t ref_cnt;
+	qdf_atomic_t del_ref_cnt;
 	qdf_atomic_t access_list[PEER_DEBUG_ID_MAX];
 	qdf_atomic_t delete_in_progress;
 	qdf_atomic_t flush_in_progress;
@@ -1463,6 +1468,8 @@ struct ol_txrx_peer_t {
 	TAILQ_ENTRY(ol_txrx_peer_t) peer_list_elem;
 	/* node in the hash table bin's list of peers */
 	TAILQ_ENTRY(ol_txrx_peer_t) hash_list_elem;
+	/* node in the pdev's inactive list of peers */
+	TAILQ_ENTRY(ol_txrx_peer_t)inactive_peer_list_elem;
 
 	/*
 	 * per TID info -