[PATCH v3 02/14] net/enetc: add TSO support for ENETC4 VF

Gagandeep Singh <[email protected]>
Newsgroups org.dpdk.dev
Message-ID <[email protected]>
Add TCP and UDP segmentation offload (TSO) for the ENETC4 virtual
function. The offload is advertised through the VF Tx capabilities and
is enabled on a per-queue basis when an application requests the TSO
offload flags during Tx queue setup.

A dedicated Tx burst, enetc_xmit_pkts_lso(), builds the large-send
descriptor chain: a standard header BD, a 16B extension BD carrying the
segment size and frame length extension, and one BD per payload buffer
with the final-frame flag on the last BD. To make room for the extra
extension BD, an LSO-enabled Tx ring is allocated with twice the number
of descriptors. The existing non-TSO Tx paths are left unchanged.

Update the ENETC4 feature list and documentation accordingly.

Signed-off-by: Gagandeep Singh <[email protected]>
---
 doc/guides/nics/enetc4.rst             |   2 +
 doc/guides/nics/features/enetc4.ini    |   1 +
 doc/guides/rel_notes/release_26_11.rst |   1 +
 drivers/net/enetc/base/enetc4_hw.h     |  35 +++-
 drivers/net/enetc/enetc.h              |   5 +
 drivers/net/enetc/enetc4_ethdev.c      |  45 ++++-
 drivers/net/enetc/enetc4_vf.c          |   2 +
 drivers/net/enetc/enetc_rxtx.c         | 255 +++++++++++++++++++++++++
 8 files changed, 341 insertions(+), 5 deletions(-)

diff --git a/doc/guides/nics/enetc4.rst b/doc/guides/nics/enetc4.rst
index f0dd039f6e..4d27e4048e 100644
--- a/doc/guides/nics/enetc4.rst
+++ b/doc/guides/nics/enetc4.rst
@@ -53,6 +53,8 @@ Key functionality includes:
 - Receive processing: Upon packet reception, the BD Ring status bit is set,
   facilitating packet processing.
 - Transmission: Packet transmission precedes reception, ensuring efficient data transfer.
+- TCP and UDP segmentation offload (TSO) on VFs, enabled per Tx queue
+  when the TSO offload flag is requested.
 
 
 Prerequisites
diff --git a/doc/guides/nics/features/enetc4.ini b/doc/guides/nics/features/enetc4.ini
index 91b18d979e..8ec1e8d00f 100644
--- a/doc/guides/nics/features/enetc4.ini
+++ b/doc/guides/nics/features/enetc4.ini
@@ -7,6 +7,7 @@
 Link status event    = Y
 Speed capabilities   = Y
 Link status          = Y
+TSO                  = Y
 Promiscuous mode     = Y
 Allmulticast mode    = Y
 Unicast MAC filter   = Y
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 3678dd6894..6a348ab4e0 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -61,6 +61,7 @@ New Features
   Updated the NXP ENETC4 poll mode driver for i.MX95:
 
   * Added KEEP_CRC Rx offload support for the ENETC4 PMD to preserve the Ethernet FCS.
+  * Added TCP Segmentation Offload (TSO) support for the ENETC4 VF.
 
 Removed Items
 -------------
diff --git a/drivers/net/enetc/base/enetc4_hw.h b/drivers/net/enetc/base/enetc4_hw.h
index 0105549857..64c1a5e750 100644
--- a/drivers/net/enetc/base/enetc4_hw.h
+++ b/drivers/net/enetc/base/enetc4_hw.h
@@ -1,5 +1,5 @@
 /* SPDX-License-Identifier: BSD-3-Clause
- * Copyright 2024 NXP
+ * Copyright 2024-2026 NXP
  *
  * This header file defines the register offsets and bit fields
  * of ENETC4 PF and VFs.
@@ -24,8 +24,14 @@ struct enetc_msg_swbd {
 
 /* enetc4 txbd flags */
 #define ENETC4_TXBD_FLAGS_L4CS		BIT(0)
+/* Request LSO (Large Send Offload) segmentation for this frame */
+#define ENETC4_TXBD_FLAGS_LSO		BIT(1)
+/* L4 checksum insertion (also used for checksum update on LSO) */
 #define ENETC4_TXBD_FLAGS_L_TX_CKSUM	BIT(3)
+/* Extended descriptor: the next 16B ring entry is an extension BD */
+#define ENETC4_TXBD_FLAGS_EXT		BIT(6)
 #define ENETC4_TXBD_FLAGS_F		BIT(7)
+
 /* L4 type */
 #define ENETC4_TXBD_L4T_UDP		BIT(0)
 #define ENETC4_TXBD_L4T_TCP		BIT(1)
@@ -34,6 +40,33 @@ struct enetc_msg_swbd {
 /* IPv4 checksum */
 #define ENETC4_TXBD_IPCS		1
 
+/*
+ * Extension Transmit Buffer Descriptor (16B). When the standard BD has the
+ * extended flag set, this occupies the next ring entry and carries the extra
+ * fields required by LSO.
+ */
+struct enetc_tx_bd_ext {
+	uint32_t timestamp;	/* PTP timestamp, unused for LSO */
+	uint32_t vlan;		/* VLAN insert, unused for LSO */
+	uint32_t lso;		/* LSO_MAX_SEG_SIZE and FRM_LEN_EXT */
+	uint32_t flags;		/* extension flags */
+};
+
+/* LSO_MAX_SEG_SIZE occupies bits 13-0 of the LSO word */
+#define ENETC4_TXBD_EXT_LSO_SEG_MASK	0x3fff
+#define ENETC4_TXBD_EXT_LSO_SEG(mss) \
+		((uint32_t)((mss) & ENETC4_TXBD_EXT_LSO_SEG_MASK))
+/* FRM_LEN_EXT occupies bits 19-16 of the LSO word (top 4 bits of data len) */
+#define ENETC4_TXBD_EXT_FRM_LEN_EXT(x) \
+		(((uint32_t)((x) & 0xf)) << 16)
+/* Final flag in the extension flags word (bit 127 -> local bit 31) */
+#define ENETC4_TXBD_EXT_FLAGS_F		BIT(31)
+
+/* NETC does not create LSO frames larger than this many bytes */
+#define ENETC4_LSO_MAX_FRAME		9600
+/* Maximum LSO data unit (payload to be segmented) supported by HW: 256KB */
+#define ENETC4_LSO_MAX_DATA_UNIT	(256 * 1024)
+
 /***************************ENETC port registers**************************/
 #define ENETC4_PMR		0x10
 #define ENETC4_PMR_EN		(BIT(16) | BIT(17) | BIT(18))
diff --git a/drivers/net/enetc/enetc.h b/drivers/net/enetc/enetc.h
index d3a8b8e34a..db349e051b 100644
--- a/drivers/net/enetc/enetc.h
+++ b/drivers/net/enetc/enetc.h
@@ -91,6 +91,7 @@ struct enetc_bdr {
 		void *tcisr; /* Tx */
 		int next_to_alloc; /* Rx */
 	};
+
 	struct rte_mempool *mb_pool;   /* mbuf pool to populate RX ring. */
 	/* Partial scatter-gather chain persisted across burst calls. */
 	struct rte_mbuf *pkt_first_seg; /* first segment of in-progress frame */
@@ -99,6 +100,7 @@ struct enetc_bdr {
 	uint64_t ierrors;
 	uint8_t rx_deferred_start;
 	uint8_t tx_deferred_start;
+	uint8_t lso_enable;
 };
 
 struct enetc_eth_hw {
@@ -312,6 +314,9 @@ uint16_t enetc_xmit_pkts(void *txq, struct rte_mbuf **tx_pkts,
 		uint16_t nb_pkts);
 uint16_t enetc_xmit_pkts_nc(void *txq, struct rte_mbuf **tx_pkts,
 		uint16_t nb_pkts);
+uint16_t enetc_xmit_pkts_lso(void *txq, struct rte_mbuf **tx_pkts,
+		uint16_t nb_pkts);
+
 uint16_t enetc_recv_pkts(void *rxq, struct rte_mbuf **rx_pkts,
 		uint16_t nb_pkts);
 uint16_t enetc_recv_pkts_nc(void *rxq, struct rte_mbuf **rx_pkts,
diff --git a/drivers/net/enetc/enetc4_ethdev.c b/drivers/net/enetc/enetc4_ethdev.c
index 1a8056580c..897e12e21f 100644
--- a/drivers/net/enetc/enetc4_ethdev.c
+++ b/drivers/net/enetc/enetc4_ethdev.c
@@ -22,7 +22,9 @@ static uint64_t dev_rx_offloads_sup =
 static uint64_t dev_tx_offloads_sup =
 	RTE_ETH_TX_OFFLOAD_IPV4_CKSUM |
 	RTE_ETH_TX_OFFLOAD_UDP_CKSUM |
-	RTE_ETH_TX_OFFLOAD_TCP_CKSUM;
+	RTE_ETH_TX_OFFLOAD_TCP_CKSUM |
+	RTE_ETH_TX_OFFLOAD_TCP_TSO |
+	RTE_ETH_TX_OFFLOAD_UDP_TSO;
 
 #define ENETC4_TXQ_PRIORITIES	"enetc4_txq_prior"
 #define ENETC4_NC_MEMORY	"nc"
@@ -321,28 +323,38 @@ static int
 enetc4_alloc_txbdr(struct enetc_bdr *txr, uint16_t nb_desc)
 {
 	int size;
+	uint32_t ring_desc;
 
-	size = nb_desc * sizeof(struct enetc_swbd);
+	/*
+	 * On LSO-enabled rings each frame uses an extra 16B extension BD
+	 * (logical 32B descriptor) plus the payload BDs.  Size the ring with
+	 * twice the requested entries so the effective frame capacity is
+	 * preserved.  Non-LSO rings are sized exactly as requested.
+	 */
+	ring_desc = txr->lso_enable ? (uint32_t)nb_desc * 2 : (uint32_t)nb_desc;
+
+	size = ring_desc * sizeof(struct enetc_swbd);
 	/* Zero q_swbd so buffer_addr is NULL for all uninitialized slots. */
 	txr->q_swbd = rte_zmalloc(NULL, size, ENETC_BD_RING_ALIGN);
 	if (txr->q_swbd == NULL)
 		return -ENOMEM;
 
 	/* Allocate the TX BD ring: each BD is struct enetc_tx_bd (16 bytes). */
-	size = nb_desc * sizeof(struct enetc_tx_bd);
+	size = ring_desc * sizeof(struct enetc_tx_bd);
 	txr->bd_base = rte_zmalloc(NULL, size, ENETC_BD_RING_ALIGN);
 	if (txr->bd_base == NULL) {
 		rte_free(txr->q_swbd);
 		txr->q_swbd = NULL;
 		return -ENOMEM;
 	}
-	txr->bd_count = nb_desc;
+	txr->bd_count = ring_desc;
 	txr->next_to_clean = 0;
 	txr->next_to_use = 0;
 
 	return 0;
 }
 
+
 static void
 enetc4_free_bdr(struct enetc_bdr *rxr)
 {
@@ -352,6 +364,7 @@ enetc4_free_bdr(struct enetc_bdr *rxr)
 	rxr->bd_base = NULL;
 }
 
+
 static void
 enetc4_setup_txbdr(struct enetc_hw *hw, struct enetc_bdr *tx_ring)
 {
@@ -388,6 +401,7 @@ enetc4_tx_queue_setup(struct rte_eth_dev *dev,
 	struct rte_eth_dev_data *data = dev->data;
 	struct enetc_eth_adapter *priv =
 			ENETC_DEV_PRIVATE(data->dev_private);
+	uint64_t tx_offloads;
 
 	PMD_INIT_FUNC_TRACE();
 	if (nb_desc > MAX_BD_COUNT)
@@ -401,6 +415,29 @@ enetc4_tx_queue_setup(struct rte_eth_dev *dev,
 	}
 
 	tx_ring->index = queue_idx;
+	/*
+	 * LSO (TCP/UDP segmentation) is a port-level offload. The Tx burst is
+	 * a single device-level function pointer, so it must be consistent for
+	 * all queues; likewise every ring must be sized the same way. Decide
+	 * from the port offloads only (not the per-queue tx_conf) so that all
+	 * queues either use the LSO burst with a doubled ring, or none do.
+	 * The LSO burst also handles non-TSO packets, so it is safe on queues
+	 * that never carry TSO traffic. LSO needs HW FCS insertion, so it is
+	 * incompatible with KEEP_CRC on Rx.
+	 */
+	tx_offloads = data->dev_conf.txmode.offloads;
+	if (tx_offloads & (RTE_ETH_TX_OFFLOAD_TCP_TSO |
+			   RTE_ETH_TX_OFFLOAD_UDP_TSO)) {
+		if (data->dev_conf.rxmode.offloads &
+		    RTE_ETH_RX_OFFLOAD_KEEP_CRC) {
+			ENETC_PMD_ERR("LSO (TSO) is incompatible with KEEP_CRC");
+			rte_free(tx_ring);
+			return -EINVAL;
+		}
+		tx_ring->lso_enable = 1;
+		dev->tx_pkt_burst = &enetc_xmit_pkts_lso;
+	}
+
 	err = enetc4_alloc_txbdr(tx_ring, nb_desc);
 	if (err)
 		goto fail;
diff --git a/drivers/net/enetc/enetc4_vf.c b/drivers/net/enetc/enetc4_vf.c
index 83a1e4931f..3083a56335 100644
--- a/drivers/net/enetc/enetc4_vf.c
+++ b/drivers/net/enetc/enetc4_vf.c
@@ -60,6 +60,8 @@ static uint64_t dev_tx_offloads_sup =
 	RTE_ETH_TX_OFFLOAD_IPV4_CKSUM |
 	RTE_ETH_TX_OFFLOAD_UDP_CKSUM |
 	RTE_ETH_TX_OFFLOAD_TCP_CKSUM |
+	RTE_ETH_TX_OFFLOAD_TCP_TSO |
+	RTE_ETH_TX_OFFLOAD_UDP_TSO |
 	RTE_ETH_TX_OFFLOAD_MULTI_SEGS;
 
 static void
diff --git a/drivers/net/enetc/enetc_rxtx.c b/drivers/net/enetc/enetc_rxtx.c
index e3bef607dd..f19f32eace 100644
--- a/drivers/net/enetc/enetc_rxtx.c
+++ b/drivers/net/enetc/enetc_rxtx.c
@@ -221,6 +221,261 @@ enetc_xmit_pkts_nc(void *tx_queue,
 	return start;
 }
 
+/*
+ * LSO (Large Send Offload) Tx burst.
+ *
+ * Dedicated burst used on Tx rings with LSO enabled (txr->lso_enable). It
+ * follows the same non-cache-coherent discipline as enetc_xmit_pkts_cacheable:
+ * payload lines are flushed with dcbf before handing the frame to HW and the
+ * written BD cache lines are flushed at the end of the batch.
+ *
+ * A TSO/USO frame uses an extended Tx descriptor: the standard BD points at
+ * the L2/L3/L4 header template (BUF_LEN = header length, FRM_LEN = payload
+ * length), followed by an extension BD in the next ring slot holding the
+ * segment size, then one BD per payload buffer with the F flag on the last.
+ * Non-TSO packets fall through to the normal encoding so mixed traffic works.
+ */
+uint16_t
+enetc_xmit_pkts_lso(void *tx_queue,
+		struct rte_mbuf **tx_pkts,
+		uint16_t nb_pkts)
+{
+	int i, start, bds_to_use;
+	struct enetc_tx_bd *txbd = NULL;
+	struct enetc_tx_bd_ext *txbd_ext;
+	struct enetc_bdr *tx_ring = (struct enetc_bdr *)tx_queue;
+	unsigned int j;
+	uint8_t *data;
+	struct rte_mbuf *seg;
+	uint16_t seg_len, segs_per_pkt;
+	bool is_first_seg;
+	int first_bd_idx, bd_count;
+
+	i = tx_ring->next_to_use;
+	bds_to_use = enetc_bd_unused(tx_ring);
+	bd_count = tx_ring->bd_count;
+	start = 0;
+
+	first_bd_idx = i;
+
+	while (start < nb_pkts) {
+		seg = tx_pkts[start];
+		segs_per_pkt = seg->nb_segs;
+
+		if (seg->ol_flags &
+		    (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG)) {
+			bool is_udp_seg =
+				!!(seg->ol_flags & RTE_MBUF_F_TX_UDP_SEG);
+			uint32_t hdr_len = seg->l2_len + seg->l3_len +
+					   seg->l4_len;
+			uint32_t data_unit;
+			uint16_t first_payload;
+			struct rte_mbuf *dseg;
+			int bds_needed;
+
+			/* Header BD + extension BD + one BD per segment. */
+			bds_needed = 2 + segs_per_pkt;
+			if (bds_to_use < bds_needed)
+				break;
+
+			/*
+			 * Skip frames with nothing to segment, or whose
+			 * headers are not fully contained in the first
+			 * segment. The first BD carries the header template
+			 * and first_payload is computed as (first segment
+			 * data_len - hdr_len), so the L2/L3/L4 headers must
+			 * reside contiguously in the first mbuf; otherwise the
+			 * unsigned subtraction would underflow.
+			 */
+			if (unlikely(hdr_len >= rte_pktmbuf_pkt_len(seg) ||
+				     hdr_len > rte_pktmbuf_data_len(seg))) {
+				start++;
+				continue;
+			}
+			data_unit = rte_pktmbuf_pkt_len(seg) - hdr_len;
+
+			/*
+			 * Skip frames that violate HW LSO limits: a zero
+			 * segment size, a payload larger than the HW data
+			 * unit, or a per-segment frame (headers + segment)
+			 * bigger than the maximum LSO frame size.
+			 */
+			if (unlikely(seg->tso_segsz == 0 ||
+				     data_unit > ENETC4_LSO_MAX_DATA_UNIT ||
+				     hdr_len + seg->tso_segsz >
+					     ENETC4_LSO_MAX_FRAME)) {
+				start++;
+				continue;
+			}
+
+			/* Standard (first) BD: header template only. */
+			data = rte_pktmbuf_mtod(seg, void *);
+
+			seg_len = rte_pktmbuf_data_len(seg);
+			for (j = 0; j < seg_len; j += RTE_CACHE_LINE_SIZE)
+				dcbf(data + j);
+			dcbf(data + (seg_len - 1));
+
+			txbd = ENETC_TXBD(*tx_ring, i);
+			memset(txbd, 0, sizeof(*txbd));
+			tx_ring->q_swbd[i].buffer_addr = seg;
+
+			/* FRM_LEN low 16 bits = payload length, BUF_LEN = hdr. */
+			txbd->frm_len = rte_cpu_to_le_16(data_unit & 0xffff);
+			txbd->buf_len = rte_cpu_to_le_16((uint16_t)hdr_len);
+			txbd->addr = rte_cpu_to_le_64(rte_mbuf_data_iova(seg));
+
+			/* Checksum control for the per-segment L3/L4 rewrite. */
+			txbd->l3_start = seg->l2_len;
+			txbd->l3_hdr_size = seg->l3_len / 4;
+			if (seg->ol_flags & RTE_MBUF_F_TX_IPV6) {
+				txbd->l3t = 1;
+			} else {
+				txbd->l3t = ENETC4_TXBD_L3T;
+				txbd->ipcs = ENETC4_TXBD_IPCS;
+			}
+			txbd->l4t = is_udp_seg ? ENETC4_TXBD_L4T_UDP :
+						 ENETC4_TXBD_L4T_TCP;
+			txbd->flags = ENETC4_TXBD_FLAGS_L_TX_CKSUM |
+				      ENETC4_TXBD_FLAGS_L4CS |
+				      ENETC4_TXBD_FLAGS_LSO |
+				      ENETC4_TXBD_FLAGS_EXT;
+
+			i++;
+			bds_to_use--;
+			if (unlikely(i == bd_count))
+				i = 0;
+
+			/* Extension BD occupies the next ring slot. */
+			tx_ring->q_swbd[i].buffer_addr = NULL;
+			txbd_ext = (struct enetc_tx_bd_ext *)
+				   ENETC_TXBD(*tx_ring, i);
+			memset(txbd_ext, 0, sizeof(*txbd_ext));
+			txbd_ext->lso = rte_cpu_to_le_32(ENETC4_TXBD_EXT_LSO_SEG(seg->tso_segsz) |
+					ENETC4_TXBD_EXT_FRM_LEN_EXT(data_unit >> 16));
+
+			i++;
+			bds_to_use--;
+			if (unlikely(i == bd_count))
+				i = 0;
+
+			/*
+			 * Payload BDs. The first segment's payload starts after
+			 * the header template; remaining segments are pure
+			 * payload.
+			 */
+			first_payload = (uint16_t)(seg_len - hdr_len);
+			dseg = seg;
+			is_first_seg = true;
+			while (dseg) {
+				uint16_t dlen;
+				uint64_t daddr;
+
+				if (is_first_seg) {
+					dlen = first_payload;
+					daddr = rte_mbuf_data_iova(dseg) +
+						hdr_len;
+					is_first_seg = false;
+				} else {
+					dlen = rte_pktmbuf_data_len(dseg);
+					data = rte_pktmbuf_mtod(dseg, void *);
+					for (j = 0; j < dlen;
+					     j += RTE_CACHE_LINE_SIZE)
+						dcbf(data + j);
+					dcbf(data + (dlen - 1));
+					daddr = rte_mbuf_data_iova(dseg);
+				}
+
+				if (dlen == 0) {
+					dseg = dseg->next;
+					continue;
+				}
+
+				tx_ring->q_swbd[i].buffer_addr = NULL;
+				txbd = ENETC_TXBD(*tx_ring, i);
+				memset(txbd, 0, sizeof(*txbd));
+				txbd->buf_len = rte_cpu_to_le_16(dlen);
+				txbd->addr = rte_cpu_to_le_64(daddr);
+				i++;
+				bds_to_use--;
+				if (unlikely(i == bd_count))
+					i = 0;
+				dseg = dseg->next;
+			}
+
+			/* Mark the last written BD as frame-last. */
+			txbd->flags |= ENETC4_TXBD_FLAGS_F;
+			start++;
+			continue;
+		}
+
+		/* Non-TSO packet: standard single/multi-seg encoding. */
+		if (bds_to_use < segs_per_pkt)
+			break;
+
+		is_first_seg = true;
+		while (seg) {
+			tx_ring->q_swbd[i].buffer_addr = NULL;
+			seg_len = rte_pktmbuf_data_len(seg);
+			data = rte_pktmbuf_mtod(seg, void *);
+
+			for (j = 0; j < seg_len; j += RTE_CACHE_LINE_SIZE)
+				dcbf(data + j);
+			dcbf(data + (seg_len - 1));
+
+			txbd = ENETC_TXBD(*tx_ring, i);
+			txbd->flags = 0;
+			if (is_first_seg) {
+				tx_ring->q_swbd[i].buffer_addr = seg;
+				txbd->frm_len = rte_pktmbuf_pkt_len(seg);
+				if (seg->ol_flags & ENETC4_TX_CKSUM_OFFLOAD_MASK)
+					enetc4_tx_offload_checksum(seg, txbd);
+				is_first_seg = false;
+			}
+
+			txbd->buf_len = rte_cpu_to_le_16(seg_len);
+			txbd->addr = rte_cpu_to_le_64(rte_mbuf_data_iova(seg));
+			seg = seg->next;
+			i++;
+			bds_to_use--;
+
+			if (unlikely(i == bd_count))
+				i = 0;
+		}
+
+		txbd->flags |= rte_cpu_to_le_16(ENETC4_TXBD_FLAGS_F);
+		start++;
+	}
+
+	/*
+	 * Flush TX BDs to PoC so HW (non-cache-coherent i.MX95) can read the
+	 * descriptors from memory.  Same discipline as enetc_xmit_pkts_cacheable:
+	 * walk from the cache-line-aligned start of first_bd_idx to just past
+	 * the last written BD, one dcbf per 64-byte (4-BD) line.
+	 */
+	if (likely(start > 0)) {
+		int n = first_bd_idx & ~ENETC_BD_PER_CL_MASK;
+		int written = (i - n + bd_count) % bd_count;
+
+		if (written == 0)
+			written = bd_count;
+		written = (written + ENETC_BD_PER_CL_MASK) &
+			  ~ENETC_BD_PER_CL_MASK;
+
+		while (written > 0) {
+			dcbf((void *)ENETC_TXBD(*tx_ring, n));
+			n = (n + ENETC_BD_PER_CL) % bd_count;
+			written -= ENETC_BD_PER_CL;
+		}
+	}
+
+	enetc_clean_tx_ring(tx_ring);
+	tx_ring->next_to_use = i;
+	enetc_wr_reg(tx_ring->tcir, i);
+
+	return start;
+}
+
 int
 enetc_refill_rx_ring(struct enetc_bdr *rx_ring, const int buff_cnt)
 {
-- 
2.25.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.