Selaa lähdekoodia

qcacmn: WAR for REO queue deletion race with HW

Defer freeing of REO queue descriptors to avoid corruption due to possible cache
flush by REO HW of the queue descriptors that are already removed from a peer.

Change-Id: I4b5ef8ef4fe6a11f5faef4f1f14ce27de77768e0
Karunakar Dasineni 8 vuotta sitten
vanhempi
sitoutus
8bebb003a4
3 muutettua tiedostoa jossa 114 lisäystä ja 22 poistoa
  1. 30 0
      dp/wifi3.0/dp_main.c
  2. 73 22
      dp/wifi3.0/dp_peer.c
  3. 11 0
      dp/wifi3.0/dp_types.h

+ 30 - 0
dp/wifi3.0/dp_main.c

@@ -1117,6 +1117,30 @@ static void dp_pdev_detach_wifi3(struct cdp_pdev *txrx_pdev, int force)
 	qdf_mem_free(pdev);
 }
 
+/*
+ * dp_reo_desc_freelist_destroy() - Flush REO descriptors from deferred freelist
+ * @soc: DP SOC handle
+ */
+static inline void dp_reo_desc_freelist_destroy(struct dp_soc *soc)
+{
+	struct reo_desc_list_node *desc;
+	struct dp_rx_tid *rx_tid;
+
+	qdf_spin_lock_bh(&soc->reo_desc_freelist_lock);
+	while (qdf_list_remove_front(&soc->reo_desc_freelist,
+		(qdf_list_node_t **)&desc) == QDF_STATUS_SUCCESS) {
+		rx_tid = &desc->rx_tid;
+		qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
+			rx_tid->hw_qdesc_alloc_size,
+			rx_tid->hw_qdesc_vaddr_unaligned,
+			rx_tid->hw_qdesc_paddr_unaligned, 0);
+		qdf_mem_free(desc);
+	}
+	qdf_spin_unlock_bh(&soc->reo_desc_freelist_lock);
+	qdf_list_destroy(&soc->reo_desc_freelist);
+	qdf_spinlock_destroy(&soc->reo_desc_freelist_lock);
+}
+
 /*
  * dp_soc_detach_wifi3() - Detach txrx SOC
  * @txrx_soc: DP SOC handle
@@ -1190,10 +1214,13 @@ static void dp_soc_detach_wifi3(void *txrx_soc)
 	/* REO command and status rings */
 	dp_srng_cleanup(soc, &soc->reo_cmd_ring, REO_CMD, 0);
 	dp_srng_cleanup(soc, &soc->reo_status_ring, REO_STATUS, 0);
+
 	qdf_spinlock_destroy(&soc->rx.reo_cmd_lock);
 
 	qdf_spinlock_destroy(&soc->peer_ref_mutex);
 	htt_soc_detach(soc->htt_handle);
+
+	dp_reo_desc_freelist_destroy(soc);
 }
 
 /*
@@ -1994,6 +2021,9 @@ void *dp_soc_attach_wifi3(void *osif_soc, void *hif_handle,
 		goto fail2;
 	}
 
+	qdf_spinlock_create(&soc->reo_desc_freelist_lock);
+	qdf_list_create(&soc->reo_desc_freelist, REO_DESC_FREELIST_SIZE);
+
 	return (void *)soc;
 
 fail2:

+ 73 - 22
dp/wifi3.0/dp_peer.c

@@ -491,6 +491,53 @@ static int dp_rx_tid_update_wifi3(struct dp_peer *peer, int tid, uint32_t
 	return 0;
 }
 
+/*
+ * dp_reo_desc_free() - Add reo descriptor to deferred freelist and free any
+ * aged out descriptors
+ *
+ * @soc: DP SOC handle
+ * @freedesc: REO descriptor to be freed
+ */
+static void dp_reo_desc_free(struct dp_soc *soc,
+	struct reo_desc_list_node *freedesc)
+{
+	uint32_t list_size;
+	struct reo_desc_list_node *desc;
+	unsigned long curr_ts = qdf_get_system_timestamp();
+
+	qdf_spin_lock_bh(&soc->reo_desc_freelist_lock);
+	freedesc->free_ts = curr_ts;
+	qdf_list_insert_back_size(&soc->reo_desc_freelist,
+		(qdf_list_node_t *)freedesc, &list_size);
+
+	while ((qdf_list_peek_front(&soc->reo_desc_freelist,
+		(qdf_list_node_t **)&desc) == QDF_STATUS_SUCCESS) &&
+		((list_size >= REO_DESC_FREELIST_SIZE) ||
+		((curr_ts - desc->free_ts) > REO_DESC_FREE_DEFER_MS))) {
+		struct dp_rx_tid *rx_tid;
+
+		qdf_list_remove_front(&soc->reo_desc_freelist,
+				(qdf_list_node_t **)&desc);
+		list_size--;
+		rx_tid = &desc->rx_tid;
+	/* Calling qdf_mem_free_consistent() in MCL is resulting in kernel BUG.
+	 * Diasble this temporarily.
+	 */
+#ifndef QCA_WIFI_NAPIER_EMULATION
+		qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
+			rx_tid->hw_qdesc_alloc_size,
+			rx_tid->hw_qdesc_vaddr_unaligned,
+			rx_tid->hw_qdesc_paddr_unaligned, 0);
+#endif
+		qdf_mem_free(desc);
+
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
+			"%s: Freed: %p\n",
+			__func__, desc);
+	}
+	qdf_spin_unlock_bh(&soc->reo_desc_freelist_lock);
+}
+
 /*
  * dp_rx_tid_setup_wifi3() – Setup receive TID state
  * @peer: Datapath peer handle
@@ -533,6 +580,7 @@ int dp_rx_tid_setup_wifi3(struct dp_peer *peer, int tid,
 		hw_qdesc_size = hal_get_reo_qdesc_size(soc->hal_soc,
 			ba_window_size);
 #endif
+
 	hw_qdesc_align = hal_get_reo_qdesc_align(soc->hal_soc);
 	/* To avoid unnecessary extra allocation for alignment, try allocating
 	 * exact size and see if we already have aligned address.
@@ -626,7 +674,8 @@ int dp_rx_tid_setup_wifi3(struct dp_peer *peer, int tid,
 static void dp_rx_tid_delete_cb(struct dp_soc *soc, void *cb_ctxt,
 	union hal_reo_status *reo_status)
 {
-	struct dp_rx_tid *rx_tid = (struct dp_rx_tid *)cb_ctxt;
+	struct reo_desc_list_node *freedesc =
+		(struct reo_desc_list_node *)cb_ctxt;
 
 	if (reo_status->rx_queue_status.header.status) {
 		/* Should not happen normally. Just print error for now */
@@ -634,24 +683,15 @@ static void dp_rx_tid_delete_cb(struct dp_soc *soc, void *cb_ctxt,
 			"%s: Rx tid HW desc deletion failed(%d): tid %d\n",
 			__func__,
 			reo_status->rx_queue_status.header.status,
-			rx_tid->tid);
+			freedesc->rx_tid.tid);
 	}
 
 	QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
 		"%s: rx_tid: %d status: %d\n", __func__,
-		rx_tid->tid, reo_status->rx_queue_status.header.status);
-
-	/* Calling qdf_mem_free_consistent() in MCL is resulting in kernel BUG.
-	 * Diasble this temporarily.
-	 */
-#ifndef QCA_WIFI_NAPIER_EMULATION
-	qdf_mem_free_consistent(soc->osdev, soc->osdev->dev,
-		rx_tid->hw_qdesc_alloc_size,
-		rx_tid->hw_qdesc_vaddr_unaligned,
-		rx_tid->hw_qdesc_paddr_unaligned, 0);
-#endif
+		freedesc->rx_tid.tid,
+		reo_status->rx_queue_status.header.status);
 
-	qdf_mem_free(rx_tid);
+	dp_reo_desc_free(soc, freedesc);
 }
 
 /*
@@ -666,29 +706,40 @@ static int dp_rx_tid_delete_wifi3(struct dp_peer *peer, int tid)
 	struct dp_rx_tid *rx_tid = &(peer->rx_tid[tid]);
 	struct dp_soc *soc = peer->vdev->pdev->soc;
 	struct hal_reo_cmd_params params;
-	struct dp_rx_tid *rx_tid_copy = qdf_mem_malloc(sizeof(*rx_tid_copy));
-	if (!rx_tid_copy) {
+	struct reo_desc_list_node *freedesc =
+		qdf_mem_malloc(sizeof(*freedesc));
+	if (!freedesc) {
 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-			"%s: malloc failed for rx_tid_copy: tid %d\n",
-			__func__, rx_tid->tid);
+			"%s: malloc failed for freedesc: tid %d\n",
+			__func__, tid);
 		return -ENOMEM;
 	}
 
-	*rx_tid_copy = *rx_tid;
+	freedesc->rx_tid = *rx_tid;
 
 	qdf_mem_zero(&params, sizeof(params));
 
-	params.std.need_status = 1;
+	params.std.need_status = 0;
 	params.std.addr_lo = rx_tid->hw_qdesc_paddr & 0xffffffff;
 	params.std.addr_hi = (uint64_t)(rx_tid->hw_qdesc_paddr) >> 32;
 	params.u.upd_queue_params.update_vld = 1;
 	params.u.upd_queue_params.vld = 0;
 
+	dp_reo_send_cmd(soc, CMD_UPDATE_RX_REO_QUEUE, &params, NULL, NULL);
+
+	/* Flush and invalidate the REO descriptor from HW cache */
+	qdf_mem_zero(&params, sizeof(params));
+	params.std.need_status = 1;
+	params.std.addr_lo = rx_tid->hw_qdesc_paddr & 0xffffffff;
+	params.std.addr_hi = (uint64_t)(rx_tid->hw_qdesc_paddr) >> 32;
+
+	dp_reo_send_cmd(soc, CMD_FLUSH_CACHE, &params, dp_rx_tid_delete_cb,
+		(void *)freedesc);
+
 	rx_tid->hw_qdesc_vaddr_unaligned = NULL;
 	rx_tid->hw_qdesc_alloc_size = 0;
+	rx_tid->hw_qdesc_paddr = 0;
 
-	dp_reo_send_cmd(soc, CMD_UPDATE_RX_REO_QUEUE, &params,
-		dp_rx_tid_delete_cb, (void *)rx_tid_copy);
 	return 0;
 }
 

+ 11 - 0
dp/wifi3.0/dp_types.h

@@ -24,6 +24,7 @@
 #include <qdf_lock.h>
 #include <qdf_atomic.h>
 #include <qdf_util.h>
+#include <qdf_list.h>
 #include <queue.h>
 #include <htt_common.h>
 
@@ -271,6 +272,14 @@ struct dp_intr {
 				to get DMA ring handles */
 };
 
+#define REO_DESC_FREELIST_SIZE 64
+#define REO_DESC_FREE_DEFER_MS 1000
+struct reo_desc_list_node {
+	qdf_list_node_t node;
+	unsigned long free_ts;
+	struct dp_rx_tid rx_tid;
+};
+
 /* SOC level structure for data path */
 struct dp_soc {
 	/* Common base structure - Should be the first member */
@@ -460,6 +469,8 @@ struct dp_soc {
 	/*interrupt timer*/
 	qdf_timer_t int_timer;
 #endif
+	qdf_list_t reo_desc_freelist;
+	qdf_spinlock_t reo_desc_freelist_lock;
 };
 
 #define MAX_RX_MAC_RINGS 2