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:
Orhan K AKYILDIZ 2017-12-19 20:32:30 -08:00 • committed by nshrivas
commit 15e2d83d23
3 changed files with 46 additions and 20 deletions

View file

@ -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 */

View file

@ -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*/

View file

@ -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);
}