From 15e2d83d234d9084513e47eb03e2a3ca4abaa3d6 Mon Sep 17 00:00:00 2001 From: Orhan K AKYILDIZ Date: Tue, 19 Dec 2017 20:32:30 -0800 Subject: [PATCH] qcacld-3.0: Fix a race btw tx and tx-compl While handling a multi-segment TSO packet, there is a race condition where, if tx complete arrives fast enough, the un-sent TSO segments may be lost forever and a previously sent segment would be attempted to be sent over. Fix the race condition. Dont use the segment after send to go to the next one. Change-Id: I4abd9d26f50c749141925894a8845cf82df4d222 CRs-Fixed: 2168778 --- core/dp/htt/htt_tx.c | 1 + core/dp/txrx/ol_tx.c | 40 +++++++++++++++++++++++++-------------- core/dp/txrx/ol_tx_desc.c | 25 ++++++++++++++++++------ 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/core/dp/htt/htt_tx.c b/core/dp/htt/htt_tx.c index 31c318afd4c9..c468d94ee71e 100644 --- a/core/dp/htt/htt_tx.c +++ b/core/dp/htt/htt_tx.c @@ -1527,6 +1527,7 @@ htt_tx_desc_fill_tso_info(htt_pdev_handle pdev, void *desc, if (tso_seg->seg.num_frags < FRAG_NUM_MAX) *word = 0; + qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_FILLHTTSEG); } #endif /* FEATURE_TSO */ diff --git a/core/dp/txrx/ol_tx.c b/core/dp/txrx/ol_tx.c index 388c1cfd1e76..8b041b34d4f2 100644 --- a/core/dp/txrx/ol_tx.c +++ b/core/dp/txrx/ol_tx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2018 The Linux Foundation. All rights reserved. * * Previously licensed under the ISC license by Qualcomm Atheros, Inc. * @@ -153,6 +153,7 @@ void ol_free_remaining_tso_segs(ol_txrx_vdev_handle vdev, } next_seg = free_seg->next; + free_seg->force_free = 1; ol_tso_free_segment(pdev, free_seg); free_seg = next_seg; } @@ -163,6 +164,7 @@ void ol_free_remaining_tso_segs(ol_txrx_vdev_handle vdev, */ while (free_seg) { next_seg = free_seg->next; + free_seg->force_free = 1; ol_tso_free_segment(pdev, free_seg); free_seg = next_seg; } @@ -697,6 +699,7 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) struct ol_txrx_msdu_info_t msdu_info; uint32_t tso_msdu_stats_idx = 0; + qdf_mem_zero(&msdu_info, sizeof(msdu_info)); msdu_info.htt.info.l2_hdr_type = vdev->pdev->htt_pkt_type; msdu_info.htt.action.tx_comp_req = 0; /* @@ -714,7 +717,7 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) msdu_info.peer = NULL; if (qdf_unlikely(ol_tx_prepare_tso(vdev, msdu, &msdu_info))) { - ol_txrx_dbg("ol_tx_prepare_tso failed\n"); + ol_txrx_err("ol_tx_prepare_tso failed\n"); TXRX_STATS_MSDU_LIST_INCR(vdev->pdev, tx.dropped.host_reject, msdu); return msdu; @@ -773,6 +776,8 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) TXRX_STATS_MSDU_INCR(pdev, tx.from_stack, msdu); if (qdf_likely(tx_desc)) { + struct qdf_tso_seg_elem_t *next_seg; + /* * if this is a jumbo nbuf, then increment the @@ -801,6 +806,17 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) sizeof(struct htt_tx_msdu_desc_ext_t); htt_tx_desc_display(tx_desc->htt_tx_desc); + + /* mark the relevant tso_seg free-able */ + if (msdu_info.tso_info.curr_seg) { + msdu_info.tso_info.curr_seg-> + sent_to_target = 1; + next_seg = msdu_info.tso_info. + curr_seg->next; + } else { + next_seg = NULL; + } + if ((0 == ce_send_fast(pdev->ce_tx_hdl, msdu, ep_id, pkt_download_len))) { struct qdf_tso_info_t *tso_info = @@ -811,8 +827,7 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) */ if (tx_desc->pkt_type == OL_TX_FRM_TSO) { - tso_info->curr_seg = - tso_info->curr_seg->next; + tso_info->curr_seg = next_seg; ol_free_remaining_tso_segs(vdev, &msdu_info, true); } @@ -827,10 +842,9 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) htt_tx_status_download_fail); return msdu; } - if (msdu_info.tso_info.curr_seg) { - msdu_info.tso_info.curr_seg = - msdu_info.tso_info.curr_seg->next; - } + if (msdu_info.tso_info.curr_seg) + msdu_info.tso_info.curr_seg = next_seg; + if (msdu_info.tso_info.is_tso) { qdf_nbuf_reset_num_frags(msdu); @@ -2103,7 +2117,7 @@ void ol_tso_seg_list_init(struct ol_txrx_pdev_t *pdev, uint32_t num_seg) c_element->cookie = TSO_SEG_MAGIC_COOKIE; #ifdef TSOSEG_DEBUG c_element->dbg.txdesc = NULL; - c_element->dbg.cur = -1; /* history empty */ + qdf_atomic_init(&c_element->dbg.cur); /* history empty */ qdf_tso_seg_dbg_record(c_element, TSOSEG_LOC_INIT1); #endif /* TSOSEG_DEBUG */ c_element->next = @@ -2126,8 +2140,7 @@ 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_init(c_element); qdf_tso_seg_dbg_record(c_element, TSOSEG_LOC_INIT2); #endif /* TSOSEG_DEBUG */ c_element->next = NULL; @@ -2167,11 +2180,10 @@ 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_tso_seg_dbg_bug("this seg already freed (double?)"); + qdf_tso_seg_dbg_bug("seg already freed (double?)"); return; } else if (c_element->cookie != TSO_SEG_MAGIC_COOKIE) { - qdf_print("this seg cookie is bad (memory corruption?)"); - QDF_BUG(0); + qdf_tso_seg_dbg_bug("seg cookie is bad (corruption?)"); return; } /* free this seg, so reset the cookie value*/ diff --git a/core/dp/txrx/ol_tx_desc.c b/core/dp/txrx/ol_tx_desc.c index c8c9d31a7051..73fff2851d94 100644 --- a/core/dp/txrx/ol_tx_desc.c +++ b/core/dp/txrx/ol_tx_desc.c @@ -895,20 +895,22 @@ struct qdf_tso_seg_elem_t *ol_tso_alloc_segment(struct ol_txrx_pdev_t *pdev) pdev->tso_seg_pool.num_free--; tso_seg = pdev->tso_seg_pool.freelist; if (tso_seg->on_freelist != 1) { - qdf_print("Do not alloc tso seg as this seg is not in freelist\n"); qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_print("tso seg alloc failed: not in freelist"); QDF_BUG(0); return NULL; } else if (tso_seg->cookie != TSO_SEG_MAGIC_COOKIE) { - qdf_print("Do not alloc tso seg as cookie is not good\n"); qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_print("tso seg alloc failed: bad cookie"); QDF_BUG(0); return NULL; } /*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); + tso_seg->sent_to_target = 0; + tso_seg->force_free = 0; pdev->tso_seg_pool.freelist = pdev->tso_seg_pool.freelist->next; + qdf_tso_seg_dbg_record(tso_seg, TSOSEG_LOC_ALLOC); } qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); @@ -932,11 +934,18 @@ 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_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); - qdf_tso_seg_dbg_bug("Do not free tso seg, already freed"); + qdf_print("Do not free tso seg, already freed"); + QDF_BUG(0); 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"); qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_print("Do not free tso seg: cookie is not good."); + QDF_BUG(0); + return; + } else if ((tso_seg->sent_to_target != 1) && + (tso_seg->force_free != 1)) { + qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); + qdf_print("Do not free tso seg: yet to be sent to target"); QDF_BUG(0); return; } @@ -948,10 +957,14 @@ void ol_tso_free_segment(struct ol_txrx_pdev_t *pdev, qdf_tso_seg_dbg_zero(tso_seg); tso_seg->next = pdev->tso_seg_pool.freelist; tso_seg->on_freelist = 1; + tso_seg->sent_to_target = 0; 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_tso_seg_dbg_record(tso_seg, tso_seg->force_free + ? TSOSEG_LOC_FORCE_FREE + : TSOSEG_LOC_FREE); + tso_seg->force_free = 0; qdf_spin_unlock_bh(&pdev->tso_seg_pool.tso_mutex); }