From e062f3588969550220c2ce6a23464355a018961f Mon Sep 17 00:00:00 2001 From: Orhan K AKYILDIZ Date: Wed, 19 Apr 2017 21:18:12 -0700 Subject: [PATCH] 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 --- core/dp/txrx/ol_tx.c | 18 ++++++++++++++++-- core/dp/txrx/ol_tx_desc.c | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/core/dp/txrx/ol_tx.c b/core/dp/txrx/ol_tx.c index 8eec8e5026..3f133ec1a3 100644 --- a/core/dp/txrx/ol_tx.c +++ b/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?)"); diff --git a/core/dp/txrx/ol_tx_desc.c b/core/dp/txrx/ol_tx_desc.c index f2b1a42e9b..348257fbc3 100644 --- a/core/dp/txrx/ol_tx_desc.c +++ b/core/dp/txrx/ol_tx_desc.c @@ -29,6 +29,7 @@ #include /* qdf_nbuf_t, etc. */ #include /* qdf_assert */ #include /* qdf_spinlock */ +#include /* qdf_tso_seg_dbg stuff */ #ifdef QCA_COMPUTE_TX_DELAY #include /* 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);