Procházet zdrojové kódy

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
Orhan K AKYILDIZ před 7 roky
rodič
revize
15e2d83d23
3 změnil soubory, kde provedl 46 přidání a 20 odebrání
  1. 1 0
      core/dp/htt/htt_tx.c
  2. 26 14
      core/dp/txrx/ol_tx.c
  3. 19 6
      core/dp/txrx/ol_tx_desc.c

+ 1 - 0
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 */
 

+ 26 - 14
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*/

+ 19 - 6
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);
 }