Browse Source

qcacld-3.0: Debug logs to detect TSO seg double free

On stability test bed a double free of TSO segment is reported via the
TX completion path. Made following changes -
 - Check for duplicate TX completions before freeing TSO segments.
 - Add debug logs to check if TX descriptors are being freed from the
   control path.

Change-Id: I30178de90cd0161001ded70adc26066adc9cbcb3
CRs-Fixed: 2006868
Mohit Khanna 8 years ago
parent
commit
c3b069bca5
4 changed files with 25 additions and 4 deletions
  1. 6 0
      core/dp/txrx/ol_tx.c
  2. 4 2
      core/dp/txrx/ol_tx_desc.c
  3. 8 2
      core/dp/txrx/ol_tx_send.c
  4. 7 0
      core/dp/txrx/ol_txrx.c

+ 6 - 0
core/dp/txrx/ol_tx.c

@@ -1911,6 +1911,7 @@ void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg)
 				   __func__, i);
 			QDF_BUG(0);
 			pdev->tso_seg_pool.pool_size = i;
+			pdev->tso_seg_pool.num_free = i;
 			qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex);
 			return;
 		}
@@ -1931,6 +1932,7 @@ void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg)
 			   __func__, i);
 		QDF_BUG(0);
 		pdev->tso_seg_pool.pool_size = i;
+		pdev->tso_seg_pool.num_free = i;
 		qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex);
 		return;
 	}
@@ -1938,6 +1940,7 @@ void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg)
 	c_element->cookie = TSO_SEG_MAGIC_COOKIE;
 	c_element->next = NULL;
 	pdev->tso_seg_pool.pool_size = num_seg;
+	pdev->tso_seg_pool.num_free = num_seg;
 	qdf_spinlock_create(&pdev->tso_seg_pool.tso_mutex);
 }
 
@@ -2021,6 +2024,7 @@ void ol_tso_num_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg)
 				__func__, i);
 			QDF_BUG(0);
 			pdev->tso_num_seg_pool.num_seg_pool_size = i;
+			pdev->tso_num_seg_pool.num_free = i;
 			qdf_spinlock_create(&pdev->tso_num_seg_pool.
 							tso_num_seg_mutex);
 			return;
@@ -2039,11 +2043,13 @@ void ol_tso_num_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg)
 			   __func__, i);
 		QDF_BUG(0);
 		pdev->tso_num_seg_pool.num_seg_pool_size = i;
+		pdev->tso_num_seg_pool.num_free = i;
 		qdf_spinlock_create(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
 		return;
 	}
 	c_element->next = NULL;
 	pdev->tso_num_seg_pool.num_seg_pool_size = num_seg;
+	pdev->tso_num_seg_pool.num_free = num_seg;
 	qdf_spinlock_create(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
 }
 

+ 4 - 2
core/dp/txrx/ol_tx_desc.c

@@ -360,10 +360,11 @@ void ol_tx_desc_free(struct ol_txrx_pdev_t *pdev, struct ol_tx_desc_t *tx_desc)
 {
 	qdf_spin_lock_bh(&pdev->tx_mutex);
 
+	ol_tx_desc_dup_detect_reset(pdev, tx_desc);
+
 	if (tx_desc->pkt_type == OL_TX_FRM_TSO)
 		ol_tx_tso_desc_free(pdev, tx_desc);
 
-	ol_tx_desc_dup_detect_reset(pdev, tx_desc);
 	ol_tx_desc_reset_pkt_type(tx_desc);
 	ol_tx_desc_reset_timestamp(tx_desc);
 
@@ -821,6 +822,7 @@ void ol_tso_free_segment(struct ol_txrx_pdev_t *pdev,
 		return;
 	}
 	/*this tso seg is now a part of freelist*/
+	qdf_mem_zero(tso_seg, sizeof(*tso_seg));
 	tso_seg->next = pdev->tso_seg_pool.freelist;
 	tso_seg->on_freelist = 1;
 	pdev->tso_seg_pool.freelist = tso_seg;
@@ -870,7 +872,7 @@ void ol_tso_num_seg_free(struct ol_txrx_pdev_t *pdev,
 	qdf_spin_lock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
 	tso_num_seg->next = pdev->tso_num_seg_pool.freelist;
 	pdev->tso_num_seg_pool.freelist = tso_num_seg;
-	pdev->tso_num_seg_pool.num_free++;
+		pdev->tso_num_seg_pool.num_free++;
 	qdf_spin_unlock_bh(&pdev->tso_num_seg_pool.tso_num_seg_mutex);
 }
 #endif

+ 8 - 2
core/dp/txrx/ol_tx_send.c

@@ -516,6 +516,7 @@ void ol_tx_discard_target_frms(ol_txrx_pdev_handle pdev)
 {
 	int i = 0;
 	struct ol_tx_desc_t *tx_desc;
+	int num_disarded = 0;
 
 	for (i = 0; i < pdev->tx_desc.pool_size; i++) {
 		tx_desc = ol_tx_desc_find(pdev, i);
@@ -528,12 +529,17 @@ void ol_tx_discard_target_frms(ol_txrx_pdev_handle pdev)
 		 */
 		if (qdf_atomic_read(&tx_desc->ref_cnt)) {
 			TXRX_PRINT(TXRX_PRINT_LEVEL_WARN,
-				   "Warning: freeing tx frame "
-				   "(no tx completion from the target)\n");
+				   "Warning: freeing tx desc %d", tx_desc->id);
 			ol_tx_desc_frame_free_nonstd(pdev,
 						     tx_desc, 1);
+			num_disarded++;
 		}
 	}
+
+	if (num_disarded)
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
+		"Warning: freed %d tx descs for which"
+		"no tx completion rcvd from the target", num_disarded);
 }
 #endif
 

+ 7 - 0
core/dp/txrx/ol_txrx.c

@@ -1731,6 +1731,7 @@ static void ol_txrx_pdev_pre_detach(struct cdp_pdev *ppdev, int force)
 {
 	struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
 	int i;
+	int num_freed_tx_desc = 0;
 
 	/* preconditions */
 	TXRX_ASSERT2(pdev);
@@ -1788,11 +1789,17 @@ static void ol_txrx_pdev_pre_detach(struct cdp_pdev *ppdev, int force)
 				   "Warning: freeing tx frame (no compltn)\n");
 			ol_tx_desc_frame_free_nonstd(pdev,
 						     tx_desc, 1);
+			num_freed_tx_desc++;
 		}
 		htt_tx_desc = tx_desc->htt_tx_desc;
 		htt_tx_desc_free(pdev->htt_pdev, htt_tx_desc);
 	}
 
+	if (num_freed_tx_desc)
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
+		"freed %d tx frames for which no resp from target",
+		num_freed_tx_desc);
+
 	ol_tx_deregister_flow_control(pdev);
 	/* Stop the communication between HTT and target at first */
 	htt_detach_target(pdev->htt_pdev);