qcacld-3.0: Fix NULL pointer dereferencing of peer due to race condition

Peer get deleted during ol_txrx_peer_detach_force_delete when
WMA_ROAM_OFFLOAD_SYNCH_IND is received. As peer deletion is
happening in different context and ol_rx_send_pktlog_event is
accessing the peer in different context, a possible race condition
has occurred which leads to NULL pointer dereferencing of peer.

Ignore the peer deletion during ol_txrx_peer_detach_force_delete and
delete it during ol_rx_peer_unmap_handler.

Change-Id: Icf252612081a41f94db6df4684348f2962b2da9d
CRs-Fixed: 2238214
diff --git a/core/dp/txrx/ol_txrx.c b/core/dp/txrx/ol_txrx.c
index d076cff..47089b9 100644
--- a/core/dp/txrx/ol_txrx.c
+++ b/core/dp/txrx/ol_txrx.c
@@ -1342,6 +1342,7 @@
 	ol_txrx_tso_stats_init(pdev);
 
 	TAILQ_INIT(&pdev->vdev_list);
+	TAILQ_INIT(&pdev->roam_stale_peer_list);
 
 	TAILQ_INIT(&pdev->req_list);
 	pdev->req_list_depth = 0;
@@ -3460,6 +3461,36 @@
 	}
 }
 
+bool ol_txrx_is_peer_eligible_for_deletion(ol_txrx_peer_handle peer)
+{
+	struct ol_txrx_vdev_t *vdev;
+	struct ol_txrx_pdev_t *pdev;
+	bool peerdel = true;
+	u_int16_t peer_id;
+	int i;
+
+	vdev = peer->vdev;
+	pdev = vdev->pdev;
+	for (i = 0; i < MAX_NUM_PEER_ID_PER_PEER; i++) {
+		peer_id = peer->peer_ids[i];
+
+		if (!pdev->peer_id_to_obj_map[peer_id].peer_ref)
+			continue;
+
+		if (pdev->peer_id_to_obj_map[peer_id].peer_ref != peer)
+			continue;
+
+		if (qdf_atomic_read(&pdev->peer_id_to_obj_map[peer_id].
+					del_peer_id_ref_cnt)) {
+			peerdel = false;
+			break;
+		}
+
+		pdev->peer_id_to_obj_map[peer_id].peer_ref = NULL;
+	}
+	return peerdel;
+}
+
 /**
  * ol_txrx_peer_release_ref() - release peer reference
  * @peer: peer handle
@@ -3657,7 +3688,31 @@
 
 		ol_txrx_dump_peer_access_list(peer);
 
-		qdf_mem_free(peer);
+		qdf_spin_lock_bh(&pdev->peer_map_unmap_lock);
+		if (ol_txrx_is_peer_eligible_for_deletion(peer)) {
+			qdf_mem_free(peer);
+		} else {
+			/*
+			 * Mark this PEER as a stale peer, to be deleted
+			 * during PEER UNMAP. Remove this peer from
+			 * roam_stale_peer_list during UNMAP.
+			 */
+			struct ol_txrx_roam_stale_peer_t *roam_stale_peer;
+
+			roam_stale_peer = qdf_mem_malloc(
+				sizeof(struct ol_txrx_roam_stale_peer_t));
+			if (roam_stale_peer) {
+				roam_stale_peer->peer = peer;
+				TAILQ_INSERT_TAIL(&pdev->roam_stale_peer_list,
+						  roam_stale_peer,
+						  next_stale_entry);
+			} else {
+				QDF_TRACE(QDF_MODULE_ID_TXRX,
+					  QDF_TRACE_LEVEL_ERROR,
+					  "No memory allocated");
+			}
+		}
+		qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
 	} else {
 		access_list = qdf_atomic_read(
 						&peer->access_list[debug_id]);