Эх сурвалжийг харах

qcacld-3.0: Add debug for TSO seg double-free

Add TSO segment debug code, in a featurized way, so that
when disabled it will tolerate redundant returns to the internal
pool and when enabled will check sanity on free, and record a
history of last 16 actions on the segment in the object.

Goes with the corresponding cmn component counterpart.

Change-Id: Ifab52fc2032da4d53b708b3c6716d5270c0690c1
CRs-Fixed: 2031485
Orhan K AKYILDIZ 8 жил өмнө
parent
commit
e062f35889

+ 16 - 2
core/dp/txrx/ol_tx.c

@@ -186,6 +186,8 @@ static inline uint8_t ol_tx_prepare_tso(ol_txrx_vdev_handle vdev,
 			struct qdf_tso_seg_elem_t *tso_seg =
 				ol_tso_alloc_segment(vdev->pdev);
 			if (tso_seg) {
+				qdf_tso_seg_dbg_record(tso_seg,
+						       TSOSEG_LOC_PREPARETSO);
 				tso_seg->next =
 					msdu_info->tso_info.tso_seg_list;
 				msdu_info->tso_info.tso_seg_list
@@ -534,6 +536,9 @@ ol_tx_prepare_ll_fast(struct ol_txrx_pdev_t *pdev,
 	tx_desc->netbuf = msdu;
 	if (msdu_info->tso_info.is_tso) {
 		tx_desc->tso_desc = msdu_info->tso_info.curr_seg;
+		qdf_tso_seg_dbg_setowner(tx_desc->tso_desc, tx_desc);
+		qdf_tso_seg_dbg_record(tx_desc->tso_desc,
+				       TSOSEG_LOC_TXPREPLLFAST);
 		tx_desc->tso_num_desc = msdu_info->tso_info.tso_num_seg_list;
 		tx_desc->pkt_type = OL_TX_FRM_TSO;
 		TXRX_STATS_MSDU_INCR(pdev, tx.tso.tso_pkts, msdu);
@@ -2013,6 +2018,11 @@ void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg)
 		/* set the freelist bit and magic cookie*/
 		c_element->on_freelist = 1;
 		c_element->cookie = TSO_SEG_MAGIC_COOKIE;
+#ifdef TSOSEG_DEBUG
+		c_element->dbg.txdesc = NULL;
+		c_element->dbg.cur    = -1; /* history empty */
+		qdf_tso_seg_dbg_record(c_element, TSOSEG_LOC_INIT1);
+#endif /* TSOSEG_DEBUG */
 		c_element->next =
 			qdf_mem_malloc(sizeof(struct qdf_tso_seg_elem_t));
 		c_element = c_element->next;
@@ -2032,6 +2042,11 @@ void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg)
 	}
 	c_element->on_freelist = 1;
 	c_element->cookie = TSO_SEG_MAGIC_COOKIE;
+#ifdef TSOSEG_DEBUG
+	c_element->dbg.txdesc = NULL;
+	c_element->dbg.cur    = -1; /* history empty */
+	qdf_tso_seg_dbg_record(c_element, TSOSEG_LOC_INIT2);
+#endif /* TSOSEG_DEBUG */
 	c_element->next = NULL;
 	pdev->tso_seg_pool.pool_size = num_seg;
 	pdev->tso_seg_pool.num_free = num_seg;
@@ -2069,8 +2084,7 @@ void ol_tso_seg_list_deinit(struct ol_txrx_pdev_t *pdev)
 	while (i-- > 0 && c_element) {
 		temp = c_element->next;
 		if (c_element->on_freelist != 1) {
-			qdf_print("this seg memory is already freed (double free?)");
-			QDF_BUG(0);
+			qdf_tso_seg_dbg_bug("this seg already freed (double?)");
 			return;
 		} else if (c_element->cookie != TSO_SEG_MAGIC_COOKIE) {
 			qdf_print("this seg cookie is bad (memory corruption?)");

+ 33 - 3
core/dp/txrx/ol_tx_desc.c

@@ -29,6 +29,7 @@
 #include <qdf_nbuf.h>           /* qdf_nbuf_t, etc. */
 #include <qdf_util.h>           /* qdf_assert */
 #include <qdf_lock.h>           /* qdf_spinlock */
+#include <qdf_trace.h>          /* qdf_tso_seg_dbg stuff */
 #ifdef QCA_COMPUTE_TX_DELAY
 #include <qdf_time.h>           /* qdf_system_ticks */
 #endif
@@ -761,6 +762,31 @@ free_tx_desc:
 }
 
 #if defined(FEATURE_TSO)
+#ifdef TSOSEG_DEBUG
+static int
+ol_tso_seg_dbg_sanitize(struct qdf_tso_seg_elem_t *tsoseg)
+{
+	int rc = -1;
+	struct ol_tx_desc_t *txdesc;
+
+	if (tsoseg != NULL) {
+		txdesc = tsoseg->dbg.txdesc;
+		if (txdesc->tso_desc != tsoseg)
+			qdf_tso_seg_dbg_bug("Owner sanity failed");
+		else
+			rc = 0;
+	}
+	return rc;
+
+};
+#else
+static int
+ol_tso_seg_dbg_sanitize(struct qdf_tso_seg_elem_t *tsoseg)
+{
+	return 0;
+}
+#endif /* TSOSEG_DEBUG */
+
 /**
  * ol_tso_alloc_segment() - function to allocate a TSO segment
  * element
@@ -792,6 +818,7 @@ struct qdf_tso_seg_elem_t *ol_tso_alloc_segment(struct ol_txrx_pdev_t *pdev)
 		}
 		/*this tso seg is not a part of freelist now.*/
 		tso_seg->on_freelist = 0;
+		qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_ALLOC);
 		pdev->tso_seg_pool.freelist = pdev->tso_seg_pool.freelist->next;
 	}
 	qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
@@ -815,9 +842,8 @@ void ol_tso_free_segment(struct ol_txrx_pdev_t *pdev,
 {
 	qdf_spin_lock_bh(&pdev->tso_seg_pool.tso_mutex);
 	if (tso_seg->on_freelist != 0) {
-		qdf_print("Do not free the tso seg as this seg is already freed");
 		qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);
-		QDF_BUG(0);
+		qdf_tso_seg_dbg_bug("Do not free tso seg, already freed");
 		return;
 	} else if (tso_seg->cookie != TSO_SEG_MAGIC_COOKIE) {
 		qdf_print("Do not free the tso seg as cookie is not good. Looks like memory corruption");
@@ -825,11 +851,15 @@ void ol_tso_free_segment(struct ol_txrx_pdev_t *pdev,
 		QDF_BUG(0);
 		return;
 	}
+	/* sanitize before free */
+	ol_tso_seg_dbg_sanitize(tso_seg);
 	/*this tso seg is now a part of freelist*/
-	qdf_mem_zero(tso_seg, sizeof(*tso_seg));
+	/* retain segment history, if debug is enabled */
+	qdf_tso_seg_dbg_zero(tso_seg);
 	tso_seg->next = pdev->tso_seg_pool.freelist;
 	tso_seg->on_freelist = 1;
 	tso_seg->cookie = TSO_SEG_MAGIC_COOKIE;
+	qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_FREE);
 	pdev->tso_seg_pool.freelist = tso_seg;
 	pdev->tso_seg_pool.num_free++;
 	qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex);