mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-14 08:36:12 -04:00
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
This commit is contained in:
parent
d9f72f1327
commit
15e2d83d23
3 changed files with 46 additions and 20 deletions
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue