[PATCH v4] net/intel: fix idpf Tx payload corruption in split queue

Shaiq Wani shaiq.wani at intel.com
Tue Oct 27 07:10:17 CET 2026


Observed on ACC while running NVMe traffic tests: under sustained
high-throughput split-queue Tx, payload bytes on the wire did not
match what the application submitted, while headers and checksums
looked valid.

The RS-completion path freed mbufs by walking sw_ring[] slots between
first_id and the EOP sw_id. RE reclaims and reuses those slots before
RS lands, so the walker freed mbufs still in flight for later packets,
whose buffers were then recycled and overwritten mid-DMA.

Track completion ownership in a shadow ring indexed by a software-
defined compl_tag written to all data descriptors of a packet (per the
IDPF spec) and echoed back by HW on RS. The shadow ring is the sole
owner of in-flight mbufs, so sw_ring[].mbuf is no longer touched and
queue stop/release drain only the shadow ring. Size it to nb_tx_desc
and stop Tx when the next compl_tag slot is still occupied, since RE
runs ahead of RS and nb_tx_free does not bound pending completions.
Allocate it at queue setup (failing on -ENOMEM) and free it on all
setup-error and release paths.

Remove the now unused ci_tx_entry.first_id field.

The scalar shadow ring and the vector sw_ring_vec are both nb_tx_desc
arrays of mbuf pointers and are never used at the same time, so place
the shadow ring in the sw_ring/sw_ring_vec union and allocate a single
nb_tx_desc-sized ring for split queues instead of the old
2 * nb_tx_desc ci_tx_entry ring. This drops split-queue ring storage
from 40 KiB to 8 KiB per queue at 1024 descriptors and keeps
ci_tx_queue from growing. Split-queue mbuf drain now follows
use_vec_entry: the vector path reclaims via ci_txq_release_all_mbufs(),
the scalar path drains the shadow ring by completion tag.

Fixes: 96cc9b6ea60c ("net/idpf: fix multi-segment mbuf leak in split Tx path")
Cc: stable at dpdk.org

Signed-off-by: Shaiq Wani <shaiq.wani at intel.com>
---
v4: fold in the reviewer's split-queue footprint optimisation:
* Move tx_pending_pkts into the sw_ring/sw_ring_vec union; for split
  queues allocate one nb_tx_desc mbuf-pointer ring instead of the
  2 * nb_tx_desc ci_tx_entry ring (40 KiB -> 8 KiB at 1024 descs).
* Drop the now-dead sw_ring next_id chain init in descq_reset and the
  2 * nb_tx_desc sw_ring sizing; split queues no longer allocate the
  scalar sw_ring/rs_last_id.
* Set use_vec_entry for split queues and route mbuf drain through it
  (vector: ci_txq_release_all_mbufs(); scalar: shadow-ring drain).
  Rename idpf_qc_split_tx_pending_free() to
  idpf_qc_split_tx_release_mbufs() to reflect it no longer frees the
  ring (freed once via the union pointer).

v3: shrink ci_tx_queue and trim the fast path per review:
* Drop the redundant tx_pending_size field (always nb_tx_desc).
* Reuse the now-dead sw_tail slot as tx_next_compl_tag.
* Remove the dead sw_ring[]/next_id walk from idpf_dp_splitq_xmit_pkts.

v2: no mbufs in sw_ring[] (shadow ring is sole owner); stop Tx on a
pending compl_tag slot; -ENOMEM propagation and cleanup on all paths;
same compl_tag on all data descs; shadow ring sized to nb_tx_desc.
---
 drivers/net/intel/common/tx.h             |   5 +-
 drivers/net/intel/cpfl/cpfl_rxtx.c        |  69 ++++++------
 drivers/net/intel/idpf/idpf_common_rxtx.c | 128 +++++++++++++---------
 drivers/net/intel/idpf/idpf_common_rxtx.h |   4 +
 drivers/net/intel/idpf/idpf_rxtx.c        |  60 +++++-----
 5 files changed, 144 insertions(+), 122 deletions(-)

diff --git a/drivers/net/intel/common/tx.h b/drivers/net/intel/common/tx.h
index 2a63cd4330..00bf90181c 100644
--- a/drivers/net/intel/common/tx.h
+++ b/drivers/net/intel/common/tx.h
@@ -166,7 +166,6 @@ struct ci_tx_queue;
 struct ci_tx_entry {
 	struct rte_mbuf *mbuf; /* mbuf associated with TX desc, if any. */
 	uint16_t next_id; /* Index of next descriptor in ring. */
-	uint16_t first_id; /* Split-queue: first sw_id of packet at EOP entry. */
 };
 
 /**
@@ -187,6 +186,8 @@ struct ci_tx_queue {
 	union {
 		struct ci_tx_entry *sw_ring; /* virtual address of SW ring */
 		struct ci_tx_entry_vec *sw_ring_vec;
+		/* IDPF split scalar: packet ownership indexed by completion tag. */
+		struct rte_mbuf **tx_pending_pkts;
 	};
 	/* Scalar TX path: Array tracking last_id at each RS threshold boundary */
 	uint16_t *rs_last_id;
@@ -260,8 +261,8 @@ struct ci_tx_queue {
 				struct idpf_complq *complq;
 				uint32_t latch_idx; /* Tx timestamp latch index */
 				uint16_t sw_nb_desc;
-				uint16_t sw_tail;
 				uint16_t rs_compl_count;
+				uint16_t tx_next_compl_tag;
 		};
 	};
 };
diff --git a/drivers/net/intel/cpfl/cpfl_rxtx.c b/drivers/net/intel/cpfl/cpfl_rxtx.c
index aba3a5e916..a358beca23 100644
--- a/drivers/net/intel/cpfl/cpfl_rxtx.c
+++ b/drivers/net/intel/cpfl/cpfl_rxtx.c
@@ -325,11 +325,12 @@ cpfl_tx_queue_release(void *txq)
 	q = &cpfl_txq->base;
 
 	if (q->complq) {
+		idpf_qc_split_tx_release_mbufs(q);
 		rte_memzone_free(q->complq->mz);
 		rte_free(q->complq);
-	}
+	} else
+		ci_txq_release_all_mbufs(q, q->use_vec_entry);
 
-	ci_txq_release_all_mbufs(q, q->use_vec_entry);
 	rte_free(q->sw_ring);
 	rte_free(q->rs_last_id);
 	rte_memzone_free(q->mz);
@@ -538,7 +539,6 @@ cpfl_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
 	const struct rte_memzone *mz;
 	struct ci_tx_queue *txq;
 	uint64_t offloads;
-	uint16_t len;
 	bool is_splitq;
 	int ret;
 
@@ -583,11 +583,7 @@ cpfl_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
 	txq->offloads = cpfl_tx_offload_convert(offloads);
 	txq->tx_deferred_start = tx_conf->tx_deferred_start;
 
-	if (is_splitq)
-		len = 2 * nb_desc;
-	else
-		len = nb_desc;
-	txq->sw_nb_desc = len;
+	txq->sw_nb_desc = nb_desc;
 
 	/* Allocate TX hardware ring descriptors. */
 	mz = cpfl_dma_zone_reserve(dev, queue_idx, nb_desc, VIRTCHNL2_QUEUE_TYPE_TX,
@@ -599,31 +595,34 @@ cpfl_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
 	txq->tx_ring_dma = mz->iova;
 	txq->mz = mz;
 
-	txq->sw_ring = rte_zmalloc_socket("cpfl tx sw ring",
-					  sizeof(struct ci_tx_entry) * len,
-					  RTE_CACHE_LINE_SIZE, socket_id);
-	if (txq->sw_ring == NULL) {
-		PMD_INIT_LOG(ERR, "Failed to allocate memory for SW TX ring");
-		ret = -ENOMEM;
-		goto err_sw_ring_alloc;
-	}
+	if (!is_splitq) {
+		txq->sw_ring = rte_zmalloc_socket("cpfl tx sw ring",
+				sizeof(struct ci_tx_entry) * nb_desc,
+				RTE_CACHE_LINE_SIZE, socket_id);
+		if (txq->sw_ring == NULL) {
+			PMD_INIT_LOG(ERR, "Failed to allocate memory for SW TX ring");
+			ret = -ENOMEM;
+			goto err_sw_ring_alloc;
+		}
 
-	/* Allocate RS last_id tracking array */
-	uint16_t num_rs_buckets = nb_desc / tx_rs_thresh;
-	txq->rs_last_id = rte_zmalloc_socket("cpfl tx rs_last_id",
-			sizeof(txq->rs_last_id[0]) * num_rs_buckets,
-			RTE_CACHE_LINE_SIZE, socket_id);
-	if (txq->rs_last_id == NULL) {
-		PMD_INIT_LOG(ERR, "Failed to allocate memory for RS last_id array");
-		ret = -ENOMEM;
-		goto err_rs_last_id_alloc;
-	}
+		/* Allocate RS last_id tracking array */
+		const uint16_t num_rs_buckets = nb_desc / tx_rs_thresh;
+		txq->rs_last_id = rte_zmalloc_socket("cpfl tx rs_last_id",
+				sizeof(txq->rs_last_id[0]) * num_rs_buckets,
+				RTE_CACHE_LINE_SIZE, socket_id);
+		if (txq->rs_last_id == NULL) {
+			PMD_INIT_LOG(ERR, "Failed to allocate memory for RS last_id array");
+			ret = -ENOMEM;
+			goto err_rs_last_id_alloc;
+		}
 
-	if (!is_splitq) {
 		txq->ci_tx_ring = mz->addr;
 		idpf_qc_single_tx_queue_reset(txq);
 	} else {
 		txq->desc_ring = mz->addr;
+		ret = idpf_qc_split_tx_pending_alloc(txq, socket_id);
+		if (ret != 0)
+			goto err_sw_ring_alloc;
 		idpf_qc_split_tx_descq_reset(txq);
 
 		/* Setup tx completion queue if split model */
@@ -1365,8 +1364,8 @@ cpfl_tx_queue_stop(struct rte_eth_dev *dev, uint16_t tx_queue_id)
 	}
 
 	txq = &cpfl_txq->base;
-	ci_txq_release_all_mbufs(txq, txq->use_vec_entry);
 	if (vport->txq_model == VIRTCHNL2_QUEUE_MODEL_SINGLE) {
+		ci_txq_release_all_mbufs(txq, txq->use_vec_entry);
 		idpf_qc_single_tx_queue_reset(txq);
 	} else {
 		if (cpfl_txq->hairpin_info.hairpin_q) {
@@ -1545,14 +1544,12 @@ cpfl_set_tx_function(struct rte_eth_dev *dev)
 					IDPF_TX_DEFAULT,
 					false);
 
-	/* Set use_vec_entry for single queue mode - only IDPF_TX_SINGLEQ uses regular entries */
-	if (vport->txq_model == VIRTCHNL2_QUEUE_MODEL_SINGLE) {
-		for (i = 0; i < dev->data->nb_tx_queues; i++) {
-			txq = dev->data->tx_queues[i];
-			if (txq == NULL)
-				continue;
-			txq->use_vec_entry = (ad->tx_func_type != IDPF_TX_SINGLEQ);
-		}
+	for (i = 0; i < dev->data->nb_tx_queues; i++) {
+		txq = dev->data->tx_queues[i];
+		if (txq == NULL)
+			continue;
+		txq->use_vec_entry = (ad->tx_func_type != IDPF_TX_SINGLEQ &&
+				ad->tx_func_type != IDPF_TX_DEFAULT);
 	}
 
 out:
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.c b/drivers/net/intel/idpf/idpf_common_rxtx.c
index 649b5d1f99..eb29024547 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.c
@@ -202,9 +202,7 @@ RTE_EXPORT_INTERNAL_SYMBOL(idpf_qc_split_tx_descq_reset)
 void
 idpf_qc_split_tx_descq_reset(struct ci_tx_queue *txq)
 {
-	struct ci_tx_entry *txe;
 	uint32_t i, size;
-	uint16_t prev;
 
 	if (txq == NULL) {
 		DRV_LOG(DEBUG, "Pointer to txq is NULL");
@@ -215,24 +213,56 @@ idpf_qc_split_tx_descq_reset(struct ci_tx_queue *txq)
 	for (i = 0; i < size; i++)
 		((volatile char *)txq->desc_ring)[i] = 0;
 
-	txe = txq->sw_ring;
-	prev = (uint16_t)(txq->sw_nb_desc - 1);
-	for (i = 0; i < txq->sw_nb_desc; i++) {
-		txe[i].mbuf = NULL;
-		txe[prev].next_id = i;
-		prev = i;
-	}
+	idpf_qc_split_tx_release_mbufs(txq);
 
 	txq->tx_tail = 0;
 
 	/* Use this as next to clean for split desc queue */
 	txq->last_desc_cleaned = 0;
-	txq->sw_tail = 0;
 	txq->nb_tx_free = txq->nb_tx_desc - 1;
 
 	txq->rs_compl_count = 0;
 	txq->tx_next_dd = txq->tx_rs_thresh - 1;
 	txq->tx_next_rs = txq->tx_rs_thresh - 1;
+
+	txq->tx_next_compl_tag = 0;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(idpf_qc_split_tx_pending_alloc)
+int
+idpf_qc_split_tx_pending_alloc(struct ci_tx_queue *txq, unsigned int socket_id)
+{
+	/* Scalar completion tags and vector descriptor indices both use nb_tx_desc pointers. */
+	txq->tx_pending_pkts = rte_zmalloc_socket("idpf_tx_pending",
+			sizeof(struct rte_mbuf *) * txq->nb_tx_desc,
+			RTE_CACHE_LINE_SIZE, socket_id);
+	if (txq->tx_pending_pkts == NULL) {
+		DRV_LOG(ERR, "Failed to alloc idpf tx_pending shadow ring");
+		return -ENOMEM;
+	}
+	txq->tx_next_compl_tag = 0;
+
+	return 0;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(idpf_qc_split_tx_release_mbufs)
+void
+idpf_qc_split_tx_release_mbufs(struct ci_tx_queue *txq)
+{
+	if (txq->tx_pending_pkts == NULL)
+		return;
+
+	if (txq->use_vec_entry) {
+		ci_txq_release_all_mbufs(txq, false);
+		return;
+	}
+
+	/* Scalar completion tags can outlive descriptor reuse, so drain every slot. */
+	for (uint16_t i = 0; i < txq->nb_tx_desc; i++) {
+		if (txq->tx_pending_pkts[i] != NULL)
+			rte_pktmbuf_free(txq->tx_pending_pkts[i]);
+		txq->tx_pending_pkts[i] = NULL;
+	}
 }
 
 RTE_EXPORT_INTERNAL_SYMBOL(idpf_qc_split_tx_complq_reset)
@@ -383,11 +413,12 @@ idpf_qc_tx_queue_release(void *txq)
 		return;
 
 	if (q->complq) {
+		idpf_qc_split_tx_release_mbufs(q);
 		rte_memzone_free(q->complq->mz);
 		rte_free(q->complq);
-	}
+	} else
+		ci_txq_release_all_mbufs(q, false);
 
-	ci_txq_release_all_mbufs(q, false);
 	rte_free(q->rs_last_id);
 	rte_free(q->sw_ring);
 	rte_memzone_free(q->mz);
@@ -787,7 +818,6 @@ idpf_split_tx_free(struct idpf_complq *cq)
 	volatile struct idpf_splitq_tx_compl_desc *compl_ring = cq->compl_ring;
 	volatile struct idpf_splitq_tx_compl_desc *txd;
 	uint16_t next = cq->tx_tail;
-	struct ci_tx_entry *txe;
 	struct ci_tx_queue *txq;
 	uint16_t gen, qid, q_head;
 	uint16_t nb_desc_clean;
@@ -824,26 +854,21 @@ idpf_split_tx_free(struct idpf_complq *cq)
 		txq->nb_tx_free += nb_desc_clean;
 		txq->last_desc_cleaned = q_head;
 		break;
-	case IDPF_TXD_COMPLT_RS:
-		/* Walk from first segment to EOP, freeing each segment. */
-		txe = &txq->sw_ring[q_head];
-		if (txe->mbuf != NULL) {
-			uint16_t first = txe->first_id;
-			uint16_t idx = first;
-			uint16_t end = (q_head + 1 == txq->sw_nb_desc) ?
-					0 : q_head + 1;
-
-			do {
-				txe = &txq->sw_ring[idx];
-				if (txe->mbuf != NULL) {
-					rte_pktmbuf_free_seg(txe->mbuf);
-					txe->mbuf = NULL;
-				}
-				idx = (idx + 1 == txq->sw_nb_desc) ?
-					0 : idx + 1;
-			} while (idx != end);
+	case IDPF_TXD_COMPLT_RS: {
+		/* Shadow ring indexed by the software-defined compl_tag is
+		 * the sole source of truth for RS-time mbuf ownership; free
+		 * the whole multi-seg chain via mbuf->next in one call.
+		 */
+		uint16_t tag = q_head;
+
+		if (unlikely(tag >= txq->nb_tx_desc)) {
+			TX_LOG(ERR, "invalid completion tag %u.", tag);
+		} else if (txq->tx_pending_pkts[tag] != NULL) {
+			rte_pktmbuf_free(txq->tx_pending_pkts[tag]);
+			txq->tx_pending_pkts[tag] = NULL;
 		}
 		break;
+	}
 	default:
 		TX_LOG(ERR, "unknown completion type.");
 		return;
@@ -915,10 +940,8 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
 	struct ci_tx_queue *txq = (struct ci_tx_queue *)tx_queue;
 	volatile struct idpf_flex_tx_sched_desc *txr;
 	volatile struct idpf_flex_tx_sched_desc *txd;
-	struct ci_tx_entry *sw_ring;
 	union ci_tx_offload tx_offload = {0};
-	struct ci_tx_entry *txe, *txn;
-	uint16_t nb_used, tx_id, sw_id;
+	uint16_t nb_used, tx_id;
 	struct rte_mbuf *tx_pkt;
 	uint16_t nb_to_clean;
 	uint16_t nb_tx = 0;
@@ -930,10 +953,7 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
 		return nb_tx;
 
 	txr = txq->desc_ring;
-	sw_ring = txq->sw_ring;
 	tx_id = txq->tx_tail;
-	sw_id = txq->sw_tail;
-	txe = &sw_ring[sw_id];
 
 	for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
 		tx_pkt = tx_pkts[nb_tx];
@@ -976,6 +996,19 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
 		if (txq->nb_tx_free < nb_used)
 			break;
 
+		/* RE reclaims descriptors before RS, so tag occupancy is the
+		 * only bound on packets awaiting RS completion.
+		 */
+		uint16_t tag = txq->tx_next_compl_tag;
+
+		if (unlikely(txq->tx_pending_pkts[tag] != NULL)) {
+			nb_to_clean = 2 * txq->tx_rs_thresh;
+			while (nb_to_clean--)
+				idpf_split_tx_free(txq->complq);
+			if (txq->tx_pending_pkts[tag] != NULL)
+				break;
+		}
+
 		if (ol_flags & CI_TX_CKSUM_OFFLOAD_MASK)
 			cmd_dtype = IDPF_TXD_FLEX_FLOW_CMD_CS_EN;
 
@@ -991,8 +1024,6 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
 				tx_id = 0;
 		}
 
-		uint16_t first_sw_id = sw_id;
-
 		do {
 			uint16_t slen = tx_pkt->data_len;
 			rte_iova_t buf_dma_addr = rte_mbuf_data_iova(tx_pkt);
@@ -1003,14 +1034,12 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
 			while ((ol_flags & RTE_MBUF_F_TX_TCP_SEG) &&
 					unlikely(slen > CI_MAX_DATA_PER_TXD)) {
 				txd = &txr[tx_id];
-				txn = &sw_ring[txe->next_id];
-				txe->mbuf = NULL;
 
 				txd->buf_addr = rte_cpu_to_le_64(buf_dma_addr);
 				txd->qw1.cmd_dtype = cmd_dtype |
 					IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
 				txd->qw1.rxr_bufsize = CI_MAX_DATA_PER_TXD;
-				txd->qw1.compl_tag = sw_id;
+				txd->qw1.compl_tag = tag;
 
 				buf_dma_addr += CI_MAX_DATA_PER_TXD;
 				slen -= CI_MAX_DATA_PER_TXD;
@@ -1018,33 +1047,29 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
 				tx_id++;
 				if (tx_id == txq->nb_tx_desc)
 					tx_id = 0;
-				sw_id = txe->next_id;
-				txe = txn;
 			}
 
 			txd = &txr[tx_id];
-			txn = &sw_ring[txe->next_id];
-			txe->mbuf = tx_pkt;
 
 			/* Setup TX descriptor */
 			txd->buf_addr = rte_cpu_to_le_64(buf_dma_addr);
 			txd->qw1.cmd_dtype = cmd_dtype |
 				IDPF_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
 			txd->qw1.rxr_bufsize = slen;
-			txd->qw1.compl_tag = sw_id;
+			/* Spec: COMPLETION_TAG must be identical on all data descs of a packet. */
+			txd->qw1.compl_tag = tag;
 			tx_id++;
 			if (tx_id == txq->nb_tx_desc)
 				tx_id = 0;
-			sw_id = txe->next_id;
-			txe = txn;
 			tx_pkt = tx_pkt->next;
 		} while (tx_pkt);
 
 		/* fill the last descriptor with End of Packet (EOP) bit */
 		txd->qw1.cmd_dtype |= IDPF_TXD_FLEX_FLOW_CMD_EOP;
 
-		/* Record first sw_id at EOP so completion can walk forward. */
-		sw_ring[txd->qw1.compl_tag].first_id = first_sw_id;
+		txq->tx_pending_pkts[tag] = tx_pkts[nb_tx];
+		if (++txq->tx_next_compl_tag == txq->nb_tx_desc)
+			txq->tx_next_compl_tag = 0;
 
 		txq->nb_tx_free = (uint16_t)(txq->nb_tx_free - nb_used);
 		txq->rs_compl_count += nb_used;
@@ -1060,7 +1085,6 @@ idpf_dp_splitq_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts,
 	if (likely(nb_tx > 0)) {
 		IDPF_PCI_REG_WRITE(txq->qtx_tail, tx_id);
 		txq->tx_tail = tx_id;
-		txq->sw_tail = sw_id;
 	}
 
 	return nb_tx;
diff --git a/drivers/net/intel/idpf/idpf_common_rxtx.h b/drivers/net/intel/idpf/idpf_common_rxtx.h
index b2d33287df..a4843c9f69 100644
--- a/drivers/net/intel/idpf/idpf_common_rxtx.h
+++ b/drivers/net/intel/idpf/idpf_common_rxtx.h
@@ -216,6 +216,10 @@ void idpf_qc_single_rx_queue_reset(struct idpf_rx_queue *rxq);
 __rte_internal
 void idpf_qc_split_tx_descq_reset(struct ci_tx_queue *txq);
 __rte_internal
+int idpf_qc_split_tx_pending_alloc(struct ci_tx_queue *txq, unsigned int socket_id);
+__rte_internal
+void idpf_qc_split_tx_release_mbufs(struct ci_tx_queue *txq);
+__rte_internal
 void idpf_qc_split_tx_complq_reset(struct idpf_complq *cq);
 __rte_internal
 void idpf_splitq_rearm_common(struct idpf_rx_queue *rx_bufq);
diff --git a/drivers/net/intel/idpf/idpf_rxtx.c b/drivers/net/intel/idpf/idpf_rxtx.c
index 077a92a8a9..3bee388c17 100644
--- a/drivers/net/intel/idpf/idpf_rxtx.c
+++ b/drivers/net/intel/idpf/idpf_rxtx.c
@@ -413,7 +413,6 @@ idpf_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
 	const struct rte_memzone *mz;
 	struct ci_tx_queue *txq;
 	uint64_t offloads;
-	uint16_t len;
 	bool is_splitq;
 	int ret;
 
@@ -456,11 +455,7 @@ idpf_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
 	txq->offloads = idpf_tx_offload_convert(offloads);
 	txq->tx_deferred_start = tx_conf->tx_deferred_start;
 
-	if (is_splitq)
-		len = 2 * nb_desc;
-	else
-		len = nb_desc;
-	txq->sw_nb_desc = len;
+	txq->sw_nb_desc = nb_desc;
 
 	/* Allocate TX hardware ring descriptors. */
 	mz = idpf_dma_zone_reserve(dev, queue_idx, nb_desc, VIRTCHNL2_QUEUE_TYPE_TX,
@@ -472,29 +467,32 @@ idpf_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
 	txq->tx_ring_dma = mz->iova;
 	txq->mz = mz;
 
-	txq->sw_ring = rte_zmalloc_socket("idpf tx sw ring",
-					  sizeof(struct ci_tx_entry) * len,
-					  RTE_CACHE_LINE_SIZE, socket_id);
-	if (txq->sw_ring == NULL) {
-		PMD_INIT_LOG(ERR, "Failed to allocate memory for SW TX ring");
-		ret = -ENOMEM;
-		goto err_sw_ring_alloc;
-	}
+	if (!is_splitq) {
+		txq->sw_ring = rte_zmalloc_socket("idpf tx sw ring",
+				sizeof(struct ci_tx_entry) * nb_desc,
+				RTE_CACHE_LINE_SIZE, socket_id);
+		if (txq->sw_ring == NULL) {
+			PMD_INIT_LOG(ERR, "Failed to allocate memory for SW TX ring");
+			ret = -ENOMEM;
+			goto err_sw_ring_alloc;
+		}
 
-	txq->rs_last_id = rte_zmalloc_socket("idpf tx rs_last_id",
-			sizeof(txq->rs_last_id[0]) * (nb_desc >> txq->log2_rs_thresh),
-			RTE_CACHE_LINE_SIZE, socket_id);
-	if (txq->rs_last_id == NULL) {
-		PMD_INIT_LOG(ERR, "Failed to allocate memory for TX RS tracking");
-		ret = -ENOMEM;
-		goto err_rs_last_id_alloc;
-	}
+		txq->rs_last_id = rte_zmalloc_socket("idpf tx rs_last_id",
+				sizeof(txq->rs_last_id[0]) * (nb_desc >> txq->log2_rs_thresh),
+				RTE_CACHE_LINE_SIZE, socket_id);
+		if (txq->rs_last_id == NULL) {
+			PMD_INIT_LOG(ERR, "Failed to allocate memory for TX RS tracking");
+			ret = -ENOMEM;
+			goto err_rs_last_id_alloc;
+		}
 
-	if (!is_splitq) {
 		txq->ci_tx_ring = mz->addr;
 		idpf_qc_single_tx_queue_reset(txq);
 	} else {
 		txq->desc_ring = mz->addr;
+		ret = idpf_qc_split_tx_pending_alloc(txq, socket_id);
+		if (ret != 0)
+			goto err_sw_ring_alloc;
 		idpf_qc_split_tx_descq_reset(txq);
 
 		/* Setup tx completion queue if split model */
@@ -733,8 +731,8 @@ idpf_tx_queue_stop(struct rte_eth_dev *dev, uint16_t tx_queue_id)
 	}
 
 	txq = dev->data->tx_queues[tx_queue_id];
-	ci_txq_release_all_mbufs(txq, false);
 	if (vport->txq_model == VIRTCHNL2_QUEUE_MODEL_SINGLE) {
+		ci_txq_release_all_mbufs(txq, false);
 		idpf_qc_single_tx_queue_reset(txq);
 	} else {
 		idpf_qc_split_tx_descq_reset(txq);
@@ -890,14 +888,12 @@ idpf_set_tx_function(struct rte_eth_dev *dev)
 					IDPF_TX_DEFAULT,
 					false);
 
-	/* Set use_vec_entry for single queue mode - only IDPF_TX_SINGLEQ uses regular entries */
-	if (vport->txq_model == VIRTCHNL2_QUEUE_MODEL_SINGLE) {
-		for (i = 0; i < dev->data->nb_tx_queues; i++) {
-			txq = dev->data->tx_queues[i];
-			if (txq == NULL)
-				continue;
-			txq->use_vec_entry = (ad->tx_func_type != IDPF_TX_SINGLEQ);
-		}
+	for (i = 0; i < dev->data->nb_tx_queues; i++) {
+		txq = dev->data->tx_queues[i];
+		if (txq == NULL)
+			continue;
+		txq->use_vec_entry = (ad->tx_func_type != IDPF_TX_SINGLEQ &&
+				ad->tx_func_type != IDPF_TX_DEFAULT);
 	}
 
 out:
-- 
2.34.1



More information about the dev mailing list