xref: /dpdk/drivers/net/gve/gve_tx.c (revision f665790a5dbad7b645ff46f31d65e977324e7bfc)
14bec2d0bSJunfeng Guo /* SPDX-License-Identifier: BSD-3-Clause
24bec2d0bSJunfeng Guo  * Copyright(C) 2022 Intel Corporation
34bec2d0bSJunfeng Guo  */
44bec2d0bSJunfeng Guo 
54bec2d0bSJunfeng Guo #include "gve_ethdev.h"
64bec2d0bSJunfeng Guo #include "base/gve_adminq.h"
74bec2d0bSJunfeng Guo 
84bec2d0bSJunfeng Guo static inline void
9a46583cfSJunfeng Guo gve_free_bulk_mbuf(struct rte_mbuf **txep, int num)
10a46583cfSJunfeng Guo {
11a46583cfSJunfeng Guo 	struct rte_mbuf *m, *free[GVE_TX_MAX_FREE_SZ];
12a46583cfSJunfeng Guo 	int nb_free = 0;
13a46583cfSJunfeng Guo 	int i, s;
14a46583cfSJunfeng Guo 
15a46583cfSJunfeng Guo 	if (unlikely(num == 0))
16a46583cfSJunfeng Guo 		return;
17a46583cfSJunfeng Guo 
18a46583cfSJunfeng Guo 	/* Find the 1st mbuf which needs to be free */
19a46583cfSJunfeng Guo 	for (s = 0; s < num; s++) {
20a46583cfSJunfeng Guo 		if (txep[s] != NULL) {
21a46583cfSJunfeng Guo 			m = rte_pktmbuf_prefree_seg(txep[s]);
22a46583cfSJunfeng Guo 			if (m != NULL)
23a46583cfSJunfeng Guo 				break;
24a46583cfSJunfeng Guo 		}
25a46583cfSJunfeng Guo 	}
26a46583cfSJunfeng Guo 
27a46583cfSJunfeng Guo 	if (s == num)
28a46583cfSJunfeng Guo 		return;
29a46583cfSJunfeng Guo 
30a46583cfSJunfeng Guo 	free[0] = m;
31a46583cfSJunfeng Guo 	nb_free = 1;
32a46583cfSJunfeng Guo 	for (i = s + 1; i < num; i++) {
33a46583cfSJunfeng Guo 		if (likely(txep[i] != NULL)) {
34a46583cfSJunfeng Guo 			m = rte_pktmbuf_prefree_seg(txep[i]);
35a46583cfSJunfeng Guo 			if (likely(m != NULL)) {
36a46583cfSJunfeng Guo 				if (likely(m->pool == free[0]->pool)) {
37a46583cfSJunfeng Guo 					free[nb_free++] = m;
38a46583cfSJunfeng Guo 				} else {
39a46583cfSJunfeng Guo 					rte_mempool_put_bulk(free[0]->pool, (void *)free, nb_free);
40a46583cfSJunfeng Guo 					free[0] = m;
41a46583cfSJunfeng Guo 					nb_free = 1;
42a46583cfSJunfeng Guo 				}
43a46583cfSJunfeng Guo 			}
44a46583cfSJunfeng Guo 			txep[i] = NULL;
45a46583cfSJunfeng Guo 		}
46a46583cfSJunfeng Guo 	}
47a46583cfSJunfeng Guo 	rte_mempool_put_bulk(free[0]->pool, (void **)free, nb_free);
48a46583cfSJunfeng Guo }
49a46583cfSJunfeng Guo 
50a46583cfSJunfeng Guo static inline void
51a46583cfSJunfeng Guo gve_tx_clean(struct gve_tx_queue *txq)
52a46583cfSJunfeng Guo {
53a46583cfSJunfeng Guo 	uint16_t mask = txq->nb_tx_desc - 1;
54a46583cfSJunfeng Guo 	uint32_t start = txq->next_to_clean & mask;
55a46583cfSJunfeng Guo 	uint32_t ntc, nb_clean, i;
56a46583cfSJunfeng Guo 	struct gve_tx_iovec *iov;
57a46583cfSJunfeng Guo 
58a46583cfSJunfeng Guo 	ntc = rte_be_to_cpu_32(rte_read32(txq->qtx_head));
59a46583cfSJunfeng Guo 	ntc = ntc & mask;
60a46583cfSJunfeng Guo 
61a46583cfSJunfeng Guo 	if (ntc == start)
62a46583cfSJunfeng Guo 		return;
63a46583cfSJunfeng Guo 
64a46583cfSJunfeng Guo 	/* if wrap around, free twice. */
65a46583cfSJunfeng Guo 	if (ntc < start) {
66a46583cfSJunfeng Guo 		nb_clean = txq->nb_tx_desc - start;
67a46583cfSJunfeng Guo 		if (nb_clean > GVE_TX_MAX_FREE_SZ)
68a46583cfSJunfeng Guo 			nb_clean = GVE_TX_MAX_FREE_SZ;
69a46583cfSJunfeng Guo 		if (txq->is_gqi_qpl) {
70a46583cfSJunfeng Guo 			for (i = start; i < start + nb_clean; i++) {
71a46583cfSJunfeng Guo 				iov = &txq->iov_ring[i];
72a46583cfSJunfeng Guo 				txq->fifo_avail += iov->iov_len;
73a46583cfSJunfeng Guo 				iov->iov_base = 0;
74a46583cfSJunfeng Guo 				iov->iov_len = 0;
75a46583cfSJunfeng Guo 			}
76a46583cfSJunfeng Guo 		} else {
77a46583cfSJunfeng Guo 			gve_free_bulk_mbuf(&txq->sw_ring[start], nb_clean);
78a46583cfSJunfeng Guo 		}
79a46583cfSJunfeng Guo 		txq->nb_free += nb_clean;
80a46583cfSJunfeng Guo 		start += nb_clean;
81a46583cfSJunfeng Guo 		if (start == txq->nb_tx_desc)
82a46583cfSJunfeng Guo 			start = 0;
83a46583cfSJunfeng Guo 		txq->next_to_clean += nb_clean;
84a46583cfSJunfeng Guo 	}
85a46583cfSJunfeng Guo 
86a46583cfSJunfeng Guo 	if (ntc > start) {
87a46583cfSJunfeng Guo 		nb_clean = ntc - start;
88a46583cfSJunfeng Guo 		if (nb_clean > GVE_TX_MAX_FREE_SZ)
89a46583cfSJunfeng Guo 			nb_clean = GVE_TX_MAX_FREE_SZ;
90a46583cfSJunfeng Guo 		if (txq->is_gqi_qpl) {
91a46583cfSJunfeng Guo 			for (i = start; i < start + nb_clean; i++) {
92a46583cfSJunfeng Guo 				iov = &txq->iov_ring[i];
93a46583cfSJunfeng Guo 				txq->fifo_avail += iov->iov_len;
94a46583cfSJunfeng Guo 				iov->iov_base = 0;
95a46583cfSJunfeng Guo 				iov->iov_len = 0;
96a46583cfSJunfeng Guo 			}
97a46583cfSJunfeng Guo 		} else {
98a46583cfSJunfeng Guo 			gve_free_bulk_mbuf(&txq->sw_ring[start], nb_clean);
99a46583cfSJunfeng Guo 		}
100a46583cfSJunfeng Guo 		txq->nb_free += nb_clean;
101a46583cfSJunfeng Guo 		txq->next_to_clean += nb_clean;
102a46583cfSJunfeng Guo 	}
103a46583cfSJunfeng Guo }
104a46583cfSJunfeng Guo 
105a46583cfSJunfeng Guo static inline void
106a46583cfSJunfeng Guo gve_tx_clean_swr_qpl(struct gve_tx_queue *txq)
107a46583cfSJunfeng Guo {
108a46583cfSJunfeng Guo 	uint32_t start = txq->sw_ntc;
109a46583cfSJunfeng Guo 	uint32_t ntc, nb_clean;
110a46583cfSJunfeng Guo 
111a46583cfSJunfeng Guo 	ntc = txq->sw_tail;
112a46583cfSJunfeng Guo 
113a46583cfSJunfeng Guo 	if (ntc == start)
114a46583cfSJunfeng Guo 		return;
115a46583cfSJunfeng Guo 
116a46583cfSJunfeng Guo 	/* if wrap around, free twice. */
117a46583cfSJunfeng Guo 	if (ntc < start) {
118a46583cfSJunfeng Guo 		nb_clean = txq->nb_tx_desc - start;
119a46583cfSJunfeng Guo 		if (nb_clean > GVE_TX_MAX_FREE_SZ)
120a46583cfSJunfeng Guo 			nb_clean = GVE_TX_MAX_FREE_SZ;
121a46583cfSJunfeng Guo 		gve_free_bulk_mbuf(&txq->sw_ring[start], nb_clean);
122a46583cfSJunfeng Guo 
123a46583cfSJunfeng Guo 		txq->sw_nb_free += nb_clean;
124a46583cfSJunfeng Guo 		start += nb_clean;
125a46583cfSJunfeng Guo 		if (start == txq->nb_tx_desc)
126a46583cfSJunfeng Guo 			start = 0;
127a46583cfSJunfeng Guo 		txq->sw_ntc = start;
128a46583cfSJunfeng Guo 	}
129a46583cfSJunfeng Guo 
130a46583cfSJunfeng Guo 	if (ntc > start) {
131a46583cfSJunfeng Guo 		nb_clean = ntc - start;
132a46583cfSJunfeng Guo 		if (nb_clean > GVE_TX_MAX_FREE_SZ)
133a46583cfSJunfeng Guo 			nb_clean = GVE_TX_MAX_FREE_SZ;
134a46583cfSJunfeng Guo 		gve_free_bulk_mbuf(&txq->sw_ring[start], nb_clean);
135a46583cfSJunfeng Guo 		txq->sw_nb_free += nb_clean;
136a46583cfSJunfeng Guo 		start += nb_clean;
137a46583cfSJunfeng Guo 		txq->sw_ntc = start;
138a46583cfSJunfeng Guo 	}
139a46583cfSJunfeng Guo }
140a46583cfSJunfeng Guo 
141a46583cfSJunfeng Guo static inline void
142a46583cfSJunfeng Guo gve_tx_fill_pkt_desc(volatile union gve_tx_desc *desc, struct rte_mbuf *mbuf,
143a46583cfSJunfeng Guo 		     uint8_t desc_cnt, uint16_t len, uint64_t addr)
144a46583cfSJunfeng Guo {
145a46583cfSJunfeng Guo 	uint64_t csum_l4 = mbuf->ol_flags & RTE_MBUF_F_TX_L4_MASK;
146a46583cfSJunfeng Guo 	uint8_t l4_csum_offset = 0;
147a46583cfSJunfeng Guo 	uint8_t l4_hdr_offset = 0;
148a46583cfSJunfeng Guo 
149a46583cfSJunfeng Guo 	if (mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG)
150a46583cfSJunfeng Guo 		csum_l4 |= RTE_MBUF_F_TX_TCP_CKSUM;
151a46583cfSJunfeng Guo 
152a46583cfSJunfeng Guo 	switch (csum_l4) {
153a46583cfSJunfeng Guo 	case RTE_MBUF_F_TX_TCP_CKSUM:
154a46583cfSJunfeng Guo 		l4_csum_offset = offsetof(struct rte_tcp_hdr, cksum);
155a46583cfSJunfeng Guo 		l4_hdr_offset = mbuf->l2_len + mbuf->l3_len;
156a46583cfSJunfeng Guo 		break;
157a46583cfSJunfeng Guo 	case RTE_MBUF_F_TX_UDP_CKSUM:
158a46583cfSJunfeng Guo 		l4_csum_offset = offsetof(struct rte_udp_hdr, dgram_cksum);
159a46583cfSJunfeng Guo 		l4_hdr_offset = mbuf->l2_len + mbuf->l3_len;
160a46583cfSJunfeng Guo 		break;
161a46583cfSJunfeng Guo 	case RTE_MBUF_F_TX_SCTP_CKSUM:
162a46583cfSJunfeng Guo 		l4_csum_offset = offsetof(struct rte_sctp_hdr, cksum);
163a46583cfSJunfeng Guo 		l4_hdr_offset = mbuf->l2_len + mbuf->l3_len;
164a46583cfSJunfeng Guo 		break;
165a46583cfSJunfeng Guo 	}
166a46583cfSJunfeng Guo 
167a46583cfSJunfeng Guo 	if (mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG) {
168a46583cfSJunfeng Guo 		desc->pkt.type_flags = GVE_TXD_TSO | GVE_TXF_L4CSUM;
169a46583cfSJunfeng Guo 		desc->pkt.l4_csum_offset = l4_csum_offset >> 1;
170a46583cfSJunfeng Guo 		desc->pkt.l4_hdr_offset = l4_hdr_offset >> 1;
171a46583cfSJunfeng Guo 	} else if (mbuf->ol_flags & RTE_MBUF_F_TX_L4_MASK) {
172a46583cfSJunfeng Guo 		desc->pkt.type_flags = GVE_TXD_STD | GVE_TXF_L4CSUM;
173a46583cfSJunfeng Guo 		desc->pkt.l4_csum_offset = l4_csum_offset >> 1;
174a46583cfSJunfeng Guo 		desc->pkt.l4_hdr_offset = l4_hdr_offset >> 1;
175a46583cfSJunfeng Guo 	} else {
176a46583cfSJunfeng Guo 		desc->pkt.type_flags = GVE_TXD_STD;
177a46583cfSJunfeng Guo 		desc->pkt.l4_csum_offset = 0;
178a46583cfSJunfeng Guo 		desc->pkt.l4_hdr_offset = 0;
179a46583cfSJunfeng Guo 	}
180a46583cfSJunfeng Guo 	desc->pkt.desc_cnt = desc_cnt;
181a46583cfSJunfeng Guo 	desc->pkt.len = rte_cpu_to_be_16(mbuf->pkt_len);
182a46583cfSJunfeng Guo 	desc->pkt.seg_len = rte_cpu_to_be_16(len);
183a46583cfSJunfeng Guo 	desc->pkt.seg_addr = rte_cpu_to_be_64(addr);
184a46583cfSJunfeng Guo }
185a46583cfSJunfeng Guo 
186a46583cfSJunfeng Guo static inline void
187a46583cfSJunfeng Guo gve_tx_fill_seg_desc(volatile union gve_tx_desc *desc, uint64_t ol_flags,
188a46583cfSJunfeng Guo 		      union gve_tx_offload tx_offload,
189a46583cfSJunfeng Guo 		      uint16_t len, uint64_t addr)
190a46583cfSJunfeng Guo {
191a46583cfSJunfeng Guo 	desc->seg.type_flags = GVE_TXD_SEG;
192a46583cfSJunfeng Guo 	if (ol_flags & RTE_MBUF_F_TX_TCP_SEG) {
193a46583cfSJunfeng Guo 		if (ol_flags & RTE_MBUF_F_TX_IPV6)
194a46583cfSJunfeng Guo 			desc->seg.type_flags |= GVE_TXSF_IPV6;
195a46583cfSJunfeng Guo 		desc->seg.l3_offset = tx_offload.l2_len >> 1;
196a46583cfSJunfeng Guo 		desc->seg.mss = rte_cpu_to_be_16(tx_offload.tso_segsz);
197a46583cfSJunfeng Guo 	}
198a46583cfSJunfeng Guo 	desc->seg.seg_len = rte_cpu_to_be_16(len);
199a46583cfSJunfeng Guo 	desc->seg.seg_addr = rte_cpu_to_be_64(addr);
200a46583cfSJunfeng Guo }
201a46583cfSJunfeng Guo 
202a46583cfSJunfeng Guo static inline bool
203a46583cfSJunfeng Guo is_fifo_avail(struct gve_tx_queue *txq, uint16_t len)
204a46583cfSJunfeng Guo {
205a46583cfSJunfeng Guo 	if (txq->fifo_avail < len)
206a46583cfSJunfeng Guo 		return false;
207a46583cfSJunfeng Guo 	/* Don't split segment. */
208a46583cfSJunfeng Guo 	if (txq->fifo_head + len > txq->fifo_size &&
209a46583cfSJunfeng Guo 	    txq->fifo_size - txq->fifo_head + len > txq->fifo_avail)
210a46583cfSJunfeng Guo 		return false;
211a46583cfSJunfeng Guo 	return true;
212a46583cfSJunfeng Guo }
213a46583cfSJunfeng Guo static inline uint64_t
214a46583cfSJunfeng Guo gve_tx_alloc_from_fifo(struct gve_tx_queue *txq, uint16_t tx_id, uint16_t len)
215a46583cfSJunfeng Guo {
216a46583cfSJunfeng Guo 	uint32_t head = txq->fifo_head;
217a46583cfSJunfeng Guo 	uint32_t size = txq->fifo_size;
218a46583cfSJunfeng Guo 	struct gve_tx_iovec *iov;
219a46583cfSJunfeng Guo 	uint32_t aligned_head;
220a46583cfSJunfeng Guo 	uint32_t iov_len = 0;
221a46583cfSJunfeng Guo 	uint64_t fifo_addr;
222a46583cfSJunfeng Guo 
223a46583cfSJunfeng Guo 	iov = &txq->iov_ring[tx_id];
224a46583cfSJunfeng Guo 
225a46583cfSJunfeng Guo 	/* Don't split segment */
226a46583cfSJunfeng Guo 	if (head + len > size) {
227a46583cfSJunfeng Guo 		iov_len += (size - head);
228a46583cfSJunfeng Guo 		head = 0;
229a46583cfSJunfeng Guo 	}
230a46583cfSJunfeng Guo 
231a46583cfSJunfeng Guo 	fifo_addr = head;
232a46583cfSJunfeng Guo 	iov_len += len;
233a46583cfSJunfeng Guo 	iov->iov_base = head;
234a46583cfSJunfeng Guo 
235a46583cfSJunfeng Guo 	/* Re-align to a cacheline for next head */
236a46583cfSJunfeng Guo 	head += len;
237a46583cfSJunfeng Guo 	aligned_head = RTE_ALIGN(head, RTE_CACHE_LINE_SIZE);
238a46583cfSJunfeng Guo 	iov_len += (aligned_head - head);
239a46583cfSJunfeng Guo 	iov->iov_len = iov_len;
240a46583cfSJunfeng Guo 
241a46583cfSJunfeng Guo 	if (aligned_head == txq->fifo_size)
242a46583cfSJunfeng Guo 		aligned_head = 0;
243a46583cfSJunfeng Guo 	txq->fifo_head = aligned_head;
244a46583cfSJunfeng Guo 	txq->fifo_avail -= iov_len;
245a46583cfSJunfeng Guo 
246a46583cfSJunfeng Guo 	return fifo_addr;
247a46583cfSJunfeng Guo }
248a46583cfSJunfeng Guo 
249a46583cfSJunfeng Guo static inline uint16_t
250a46583cfSJunfeng Guo gve_tx_burst_qpl(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
251a46583cfSJunfeng Guo {
252a46583cfSJunfeng Guo 	union gve_tx_offload tx_offload = {0};
253a46583cfSJunfeng Guo 	volatile union gve_tx_desc *txr, *txd;
254a46583cfSJunfeng Guo 	struct gve_tx_queue *txq = tx_queue;
255a46583cfSJunfeng Guo 	struct rte_mbuf **sw_ring = txq->sw_ring;
256a46583cfSJunfeng Guo 	uint16_t mask = txq->nb_tx_desc - 1;
257a46583cfSJunfeng Guo 	uint16_t tx_id = txq->tx_tail & mask;
258a46583cfSJunfeng Guo 	uint64_t ol_flags, addr, fifo_addr;
259a46583cfSJunfeng Guo 	uint32_t tx_tail = txq->tx_tail;
260a46583cfSJunfeng Guo 	struct rte_mbuf *tx_pkt, *first;
261a46583cfSJunfeng Guo 	uint16_t sw_id = txq->sw_tail;
262a46583cfSJunfeng Guo 	uint16_t nb_used, i;
2634f6b1dd8SJunfeng Guo 	uint64_t bytes = 0;
264a46583cfSJunfeng Guo 	uint16_t nb_tx = 0;
265a46583cfSJunfeng Guo 	uint32_t hlen;
266a46583cfSJunfeng Guo 
267a46583cfSJunfeng Guo 	txr = txq->tx_desc_ring;
268a46583cfSJunfeng Guo 
269a46583cfSJunfeng Guo 	if (txq->nb_free < txq->free_thresh || txq->fifo_avail == 0)
270a46583cfSJunfeng Guo 		gve_tx_clean(txq);
271a46583cfSJunfeng Guo 
272a46583cfSJunfeng Guo 	if (txq->sw_nb_free < txq->free_thresh)
273a46583cfSJunfeng Guo 		gve_tx_clean_swr_qpl(txq);
274a46583cfSJunfeng Guo 
275a46583cfSJunfeng Guo 	for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
276a46583cfSJunfeng Guo 		tx_pkt = *tx_pkts++;
277a46583cfSJunfeng Guo 		ol_flags = tx_pkt->ol_flags;
278a46583cfSJunfeng Guo 
279a46583cfSJunfeng Guo 		if (txq->sw_nb_free < tx_pkt->nb_segs) {
280a46583cfSJunfeng Guo 			gve_tx_clean_swr_qpl(txq);
281a46583cfSJunfeng Guo 			if (txq->sw_nb_free < tx_pkt->nb_segs)
282a46583cfSJunfeng Guo 				goto end_of_tx;
283a46583cfSJunfeng Guo 		}
284a46583cfSJunfeng Guo 
285a46583cfSJunfeng Guo 		/* Even for multi-segs, use 1 qpl buf for data */
286a46583cfSJunfeng Guo 		nb_used = 1;
287a46583cfSJunfeng Guo 		if (ol_flags & RTE_MBUF_F_TX_TCP_SEG)
288a46583cfSJunfeng Guo 			nb_used++;
289a46583cfSJunfeng Guo 
290a46583cfSJunfeng Guo 		if (txq->nb_free < nb_used)
291a46583cfSJunfeng Guo 			goto end_of_tx;
292a46583cfSJunfeng Guo 
293a46583cfSJunfeng Guo 		tx_offload.l2_len = tx_pkt->l2_len;
294a46583cfSJunfeng Guo 		tx_offload.l3_len = tx_pkt->l3_len;
295a46583cfSJunfeng Guo 		tx_offload.l4_len = tx_pkt->l4_len;
296a46583cfSJunfeng Guo 		tx_offload.tso_segsz = tx_pkt->tso_segsz;
297a46583cfSJunfeng Guo 
298a46583cfSJunfeng Guo 		first = tx_pkt;
299a46583cfSJunfeng Guo 		txd = &txr[tx_id];
300a46583cfSJunfeng Guo 		hlen = ol_flags & RTE_MBUF_F_TX_TCP_SEG ?
301a46583cfSJunfeng Guo 			(uint32_t)(tx_offload.l2_len + tx_offload.l3_len + tx_offload.l4_len) :
302a46583cfSJunfeng Guo 			tx_pkt->pkt_len;
303a46583cfSJunfeng Guo 
304a46583cfSJunfeng Guo 		sw_ring[sw_id] = tx_pkt;
305a46583cfSJunfeng Guo 		if (!is_fifo_avail(txq, hlen)) {
306a46583cfSJunfeng Guo 			gve_tx_clean(txq);
307a46583cfSJunfeng Guo 			if (!is_fifo_avail(txq, hlen))
308a46583cfSJunfeng Guo 				goto end_of_tx;
309a46583cfSJunfeng Guo 		}
310a46583cfSJunfeng Guo 		addr = (uint64_t)(tx_pkt->buf_addr) + tx_pkt->data_off;
311a46583cfSJunfeng Guo 		fifo_addr = gve_tx_alloc_from_fifo(txq, tx_id, hlen);
312a46583cfSJunfeng Guo 
313a46583cfSJunfeng Guo 		/* For TSO, check if there's enough fifo space for data first */
314a46583cfSJunfeng Guo 		if (ol_flags & RTE_MBUF_F_TX_TCP_SEG) {
315a46583cfSJunfeng Guo 			if (!is_fifo_avail(txq, tx_pkt->pkt_len - hlen)) {
316a46583cfSJunfeng Guo 				gve_tx_clean(txq);
317a46583cfSJunfeng Guo 				if (!is_fifo_avail(txq, tx_pkt->pkt_len - hlen))
318a46583cfSJunfeng Guo 					goto end_of_tx;
319a46583cfSJunfeng Guo 			}
320a46583cfSJunfeng Guo 		}
321a46583cfSJunfeng Guo 		if (tx_pkt->nb_segs == 1 || ol_flags & RTE_MBUF_F_TX_TCP_SEG)
322a46583cfSJunfeng Guo 			rte_memcpy((void *)(size_t)(fifo_addr + txq->fifo_base),
323a46583cfSJunfeng Guo 				   (void *)(size_t)addr, hlen);
324a46583cfSJunfeng Guo 		else
325a46583cfSJunfeng Guo 			rte_pktmbuf_read(tx_pkt, 0, hlen,
326a46583cfSJunfeng Guo 					 (void *)(size_t)(fifo_addr + txq->fifo_base));
327a46583cfSJunfeng Guo 		gve_tx_fill_pkt_desc(txd, tx_pkt, nb_used, hlen, fifo_addr);
328a46583cfSJunfeng Guo 
329a46583cfSJunfeng Guo 		if (ol_flags & RTE_MBUF_F_TX_TCP_SEG) {
330a46583cfSJunfeng Guo 			tx_id = (tx_id + 1) & mask;
331a46583cfSJunfeng Guo 			txd = &txr[tx_id];
332a46583cfSJunfeng Guo 			addr = (uint64_t)(tx_pkt->buf_addr) + tx_pkt->data_off + hlen;
333a46583cfSJunfeng Guo 			fifo_addr = gve_tx_alloc_from_fifo(txq, tx_id, tx_pkt->pkt_len - hlen);
334a46583cfSJunfeng Guo 			if (tx_pkt->nb_segs == 1)
335a46583cfSJunfeng Guo 				rte_memcpy((void *)(size_t)(fifo_addr + txq->fifo_base),
336a46583cfSJunfeng Guo 					   (void *)(size_t)addr,
337a46583cfSJunfeng Guo 					   tx_pkt->pkt_len - hlen);
338a46583cfSJunfeng Guo 			else
339a46583cfSJunfeng Guo 				rte_pktmbuf_read(tx_pkt, hlen, tx_pkt->pkt_len - hlen,
340a46583cfSJunfeng Guo 						 (void *)(size_t)(fifo_addr + txq->fifo_base));
341a46583cfSJunfeng Guo 
342a46583cfSJunfeng Guo 			gve_tx_fill_seg_desc(txd, ol_flags, tx_offload,
343a46583cfSJunfeng Guo 					     tx_pkt->pkt_len - hlen, fifo_addr);
344a46583cfSJunfeng Guo 		}
345a46583cfSJunfeng Guo 
346a46583cfSJunfeng Guo 		/* record mbuf in sw_ring for free */
347a46583cfSJunfeng Guo 		for (i = 1; i < first->nb_segs; i++) {
348a46583cfSJunfeng Guo 			sw_id = (sw_id + 1) & mask;
349a46583cfSJunfeng Guo 			tx_pkt = tx_pkt->next;
350a46583cfSJunfeng Guo 			sw_ring[sw_id] = tx_pkt;
351a46583cfSJunfeng Guo 		}
352a46583cfSJunfeng Guo 
353a46583cfSJunfeng Guo 		sw_id = (sw_id + 1) & mask;
354a46583cfSJunfeng Guo 		tx_id = (tx_id + 1) & mask;
355a46583cfSJunfeng Guo 
356a46583cfSJunfeng Guo 		txq->nb_free -= nb_used;
357a46583cfSJunfeng Guo 		txq->sw_nb_free -= first->nb_segs;
358a46583cfSJunfeng Guo 		tx_tail += nb_used;
3594f6b1dd8SJunfeng Guo 
3604f6b1dd8SJunfeng Guo 		bytes += first->pkt_len;
361a46583cfSJunfeng Guo 	}
362a46583cfSJunfeng Guo 
363a46583cfSJunfeng Guo end_of_tx:
364a46583cfSJunfeng Guo 	if (nb_tx) {
365a46583cfSJunfeng Guo 		rte_write32(rte_cpu_to_be_32(tx_tail), txq->qtx_tail);
366a46583cfSJunfeng Guo 		txq->tx_tail = tx_tail;
367a46583cfSJunfeng Guo 		txq->sw_tail = sw_id;
3684f6b1dd8SJunfeng Guo 
369c222ea9cSLevend Sayar 		txq->stats.packets += nb_tx;
370c222ea9cSLevend Sayar 		txq->stats.bytes += bytes;
371c222ea9cSLevend Sayar 		txq->stats.errors += nb_pkts - nb_tx;
372a46583cfSJunfeng Guo 	}
373a46583cfSJunfeng Guo 
374a46583cfSJunfeng Guo 	return nb_tx;
375a46583cfSJunfeng Guo }
376a46583cfSJunfeng Guo 
377a46583cfSJunfeng Guo static inline uint16_t
378a46583cfSJunfeng Guo gve_tx_burst_ra(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
379a46583cfSJunfeng Guo {
380a46583cfSJunfeng Guo 	union gve_tx_offload tx_offload = {0};
381a46583cfSJunfeng Guo 	volatile union gve_tx_desc *txr, *txd;
382a46583cfSJunfeng Guo 	struct gve_tx_queue *txq = tx_queue;
383a46583cfSJunfeng Guo 	struct rte_mbuf **sw_ring = txq->sw_ring;
384a46583cfSJunfeng Guo 	uint16_t mask = txq->nb_tx_desc - 1;
385a46583cfSJunfeng Guo 	uint16_t tx_id = txq->tx_tail & mask;
386a46583cfSJunfeng Guo 	uint32_t tx_tail = txq->tx_tail;
387a46583cfSJunfeng Guo 	struct rte_mbuf *tx_pkt, *first;
388a46583cfSJunfeng Guo 	uint16_t nb_used, hlen, i;
389a46583cfSJunfeng Guo 	uint64_t ol_flags, addr;
3904f6b1dd8SJunfeng Guo 	uint64_t bytes = 0;
391a46583cfSJunfeng Guo 	uint16_t nb_tx = 0;
392a46583cfSJunfeng Guo 
393a46583cfSJunfeng Guo 	txr = txq->tx_desc_ring;
394a46583cfSJunfeng Guo 
395a46583cfSJunfeng Guo 	if (txq->nb_free < txq->free_thresh)
396a46583cfSJunfeng Guo 		gve_tx_clean(txq);
397a46583cfSJunfeng Guo 
398a46583cfSJunfeng Guo 	for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
399a46583cfSJunfeng Guo 		tx_pkt = *tx_pkts++;
400a46583cfSJunfeng Guo 		ol_flags = tx_pkt->ol_flags;
401a46583cfSJunfeng Guo 
402a46583cfSJunfeng Guo 		nb_used = tx_pkt->nb_segs;
403a46583cfSJunfeng Guo 		if (ol_flags & RTE_MBUF_F_TX_TCP_SEG)
404a46583cfSJunfeng Guo 			nb_used++;
405a46583cfSJunfeng Guo 
406a46583cfSJunfeng Guo 		if (txq->nb_free < nb_used)
407a46583cfSJunfeng Guo 			goto end_of_tx;
408a46583cfSJunfeng Guo 
409a46583cfSJunfeng Guo 		tx_offload.l2_len = tx_pkt->l2_len;
410a46583cfSJunfeng Guo 		tx_offload.l3_len = tx_pkt->l3_len;
411a46583cfSJunfeng Guo 		tx_offload.l4_len = tx_pkt->l4_len;
412a46583cfSJunfeng Guo 		tx_offload.tso_segsz = tx_pkt->tso_segsz;
413a46583cfSJunfeng Guo 
414a46583cfSJunfeng Guo 		first = tx_pkt;
415a46583cfSJunfeng Guo 		txd = &txr[tx_id];
416a46583cfSJunfeng Guo 
417a46583cfSJunfeng Guo 		hlen = ol_flags & RTE_MBUF_F_TX_TCP_SEG ?
418a46583cfSJunfeng Guo 			(uint32_t)(tx_offload.l2_len + tx_offload.l3_len + tx_offload.l4_len) :
419a46583cfSJunfeng Guo 			tx_pkt->pkt_len;
420a46583cfSJunfeng Guo 		/*
421a46583cfSJunfeng Guo 		 * if tso, the driver needs to fill 2 descs for 1 mbuf
422a46583cfSJunfeng Guo 		 * so only put this mbuf into the 1st tx entry in sw ring
423a46583cfSJunfeng Guo 		 */
424a46583cfSJunfeng Guo 		sw_ring[tx_id] = tx_pkt;
425a46583cfSJunfeng Guo 		addr = rte_mbuf_data_iova(tx_pkt);
426a46583cfSJunfeng Guo 		gve_tx_fill_pkt_desc(txd, tx_pkt, nb_used, hlen, addr);
427a46583cfSJunfeng Guo 
428a46583cfSJunfeng Guo 		if (ol_flags & RTE_MBUF_F_TX_TCP_SEG) {
429a46583cfSJunfeng Guo 			tx_id = (tx_id + 1) & mask;
430a46583cfSJunfeng Guo 			txd = &txr[tx_id];
431a46583cfSJunfeng Guo 			addr = rte_mbuf_data_iova(tx_pkt) + hlen;
432a46583cfSJunfeng Guo 			gve_tx_fill_seg_desc(txd, ol_flags, tx_offload,
433a46583cfSJunfeng Guo 					     tx_pkt->data_len - hlen, addr);
434a46583cfSJunfeng Guo 		}
435a46583cfSJunfeng Guo 
436a46583cfSJunfeng Guo 		for (i = 1; i < first->nb_segs; i++) {
437a46583cfSJunfeng Guo 			tx_id = (tx_id + 1) & mask;
438a46583cfSJunfeng Guo 			txd = &txr[tx_id];
439a46583cfSJunfeng Guo 			tx_pkt = tx_pkt->next;
440a46583cfSJunfeng Guo 			sw_ring[tx_id] = tx_pkt;
441a46583cfSJunfeng Guo 			addr = rte_mbuf_data_iova(tx_pkt);
442a46583cfSJunfeng Guo 			gve_tx_fill_seg_desc(txd, ol_flags, tx_offload,
443a46583cfSJunfeng Guo 					     tx_pkt->data_len, addr);
444a46583cfSJunfeng Guo 		}
445a46583cfSJunfeng Guo 		tx_id = (tx_id + 1) & mask;
446a46583cfSJunfeng Guo 
447a46583cfSJunfeng Guo 		txq->nb_free -= nb_used;
448a46583cfSJunfeng Guo 		tx_tail += nb_used;
4494f6b1dd8SJunfeng Guo 
4504f6b1dd8SJunfeng Guo 		bytes += first->pkt_len;
451a46583cfSJunfeng Guo 	}
452a46583cfSJunfeng Guo 
453a46583cfSJunfeng Guo end_of_tx:
454a46583cfSJunfeng Guo 	if (nb_tx) {
455a46583cfSJunfeng Guo 		rte_write32(rte_cpu_to_be_32(tx_tail), txq->qtx_tail);
456a46583cfSJunfeng Guo 		txq->tx_tail = tx_tail;
4574f6b1dd8SJunfeng Guo 
458c222ea9cSLevend Sayar 		txq->stats.packets += nb_tx;
459c222ea9cSLevend Sayar 		txq->stats.bytes += bytes;
460c222ea9cSLevend Sayar 		txq->stats.errors += nb_pkts - nb_tx;
461a46583cfSJunfeng Guo 	}
462a46583cfSJunfeng Guo 
463a46583cfSJunfeng Guo 	return nb_tx;
464a46583cfSJunfeng Guo }
465a46583cfSJunfeng Guo 
466a46583cfSJunfeng Guo uint16_t
467a46583cfSJunfeng Guo gve_tx_burst(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
468a46583cfSJunfeng Guo {
469a46583cfSJunfeng Guo 	struct gve_tx_queue *txq = tx_queue;
470a46583cfSJunfeng Guo 
471a46583cfSJunfeng Guo 	if (txq->is_gqi_qpl)
472a46583cfSJunfeng Guo 		return gve_tx_burst_qpl(tx_queue, tx_pkts, nb_pkts);
473a46583cfSJunfeng Guo 
474a46583cfSJunfeng Guo 	return gve_tx_burst_ra(tx_queue, tx_pkts, nb_pkts);
475a46583cfSJunfeng Guo }
476a46583cfSJunfeng Guo 
477a46583cfSJunfeng Guo static inline void
4784bec2d0bSJunfeng Guo gve_reset_txq(struct gve_tx_queue *txq)
4794bec2d0bSJunfeng Guo {
4800cfde775SJunfeng Guo 	struct rte_mbuf **sw_ring;
4814bec2d0bSJunfeng Guo 	uint32_t size, i;
4824bec2d0bSJunfeng Guo 
4834bec2d0bSJunfeng Guo 	if (txq == NULL) {
4844bec2d0bSJunfeng Guo 		PMD_DRV_LOG(ERR, "Pointer to txq is NULL");
4854bec2d0bSJunfeng Guo 		return;
4864bec2d0bSJunfeng Guo 	}
4874bec2d0bSJunfeng Guo 
4884bec2d0bSJunfeng Guo 	size = txq->nb_tx_desc * sizeof(union gve_tx_desc);
4894bec2d0bSJunfeng Guo 	for (i = 0; i < size; i++)
4904bec2d0bSJunfeng Guo 		((volatile char *)txq->tx_desc_ring)[i] = 0;
4914bec2d0bSJunfeng Guo 
4920cfde775SJunfeng Guo 	sw_ring = txq->sw_ring;
4934bec2d0bSJunfeng Guo 	for (i = 0; i < txq->nb_tx_desc; i++) {
4944bec2d0bSJunfeng Guo 		sw_ring[i] = NULL;
4954bec2d0bSJunfeng Guo 		if (txq->is_gqi_qpl) {
4964bec2d0bSJunfeng Guo 			txq->iov_ring[i].iov_base = 0;
4974bec2d0bSJunfeng Guo 			txq->iov_ring[i].iov_len = 0;
4984bec2d0bSJunfeng Guo 		}
4994bec2d0bSJunfeng Guo 	}
5004bec2d0bSJunfeng Guo 
5014bec2d0bSJunfeng Guo 	txq->tx_tail = 0;
5024bec2d0bSJunfeng Guo 	txq->nb_free = txq->nb_tx_desc - 1;
5034bec2d0bSJunfeng Guo 	txq->next_to_clean = 0;
5044bec2d0bSJunfeng Guo 
5054bec2d0bSJunfeng Guo 	if (txq->is_gqi_qpl) {
5064bec2d0bSJunfeng Guo 		txq->fifo_size = PAGE_SIZE * txq->hw->tx_pages_per_qpl;
5074bec2d0bSJunfeng Guo 		txq->fifo_avail = txq->fifo_size;
5084bec2d0bSJunfeng Guo 		txq->fifo_head = 0;
5094bec2d0bSJunfeng Guo 		txq->fifo_base = (uint64_t)(txq->qpl->mz->addr);
5104bec2d0bSJunfeng Guo 
5114bec2d0bSJunfeng Guo 		txq->sw_tail = 0;
5124bec2d0bSJunfeng Guo 		txq->sw_nb_free = txq->nb_tx_desc - 1;
5134bec2d0bSJunfeng Guo 		txq->sw_ntc = 0;
5144bec2d0bSJunfeng Guo 	}
5154bec2d0bSJunfeng Guo }
5164bec2d0bSJunfeng Guo 
5174bec2d0bSJunfeng Guo static inline void
5184bec2d0bSJunfeng Guo gve_release_txq_mbufs(struct gve_tx_queue *txq)
5194bec2d0bSJunfeng Guo {
5204bec2d0bSJunfeng Guo 	uint16_t i;
5214bec2d0bSJunfeng Guo 
5224bec2d0bSJunfeng Guo 	for (i = 0; i < txq->nb_tx_desc; i++) {
5234bec2d0bSJunfeng Guo 		if (txq->sw_ring[i]) {
5244bec2d0bSJunfeng Guo 			rte_pktmbuf_free_seg(txq->sw_ring[i]);
5254bec2d0bSJunfeng Guo 			txq->sw_ring[i] = NULL;
5264bec2d0bSJunfeng Guo 		}
5274bec2d0bSJunfeng Guo 	}
5284bec2d0bSJunfeng Guo }
5294bec2d0bSJunfeng Guo 
5304bec2d0bSJunfeng Guo void
53110d9e91aSJunfeng Guo gve_tx_queue_release(struct rte_eth_dev *dev, uint16_t qid)
5324bec2d0bSJunfeng Guo {
53310d9e91aSJunfeng Guo 	struct gve_tx_queue *q = dev->data->tx_queues[qid];
5344bec2d0bSJunfeng Guo 
5354bec2d0bSJunfeng Guo 	if (!q)
5364bec2d0bSJunfeng Guo 		return;
5374bec2d0bSJunfeng Guo 
5384bec2d0bSJunfeng Guo 	if (q->is_gqi_qpl) {
5397f369975SJoshua Washington 		gve_teardown_queue_page_list(q->hw, q->qpl);
5404bec2d0bSJunfeng Guo 		rte_free(q->iov_ring);
5414bec2d0bSJunfeng Guo 		q->qpl = NULL;
5424bec2d0bSJunfeng Guo 	}
5434bec2d0bSJunfeng Guo 
5444bec2d0bSJunfeng Guo 	gve_release_txq_mbufs(q);
5454bec2d0bSJunfeng Guo 	rte_free(q->sw_ring);
5464bec2d0bSJunfeng Guo 	rte_memzone_free(q->mz);
5474bec2d0bSJunfeng Guo 	rte_memzone_free(q->qres_mz);
5484bec2d0bSJunfeng Guo 	q->qres = NULL;
5494bec2d0bSJunfeng Guo 	rte_free(q);
5504bec2d0bSJunfeng Guo }
5514bec2d0bSJunfeng Guo 
5524bec2d0bSJunfeng Guo int
5534bec2d0bSJunfeng Guo gve_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_id, uint16_t nb_desc,
5544bec2d0bSJunfeng Guo 		   unsigned int socket_id, const struct rte_eth_txconf *conf)
5554bec2d0bSJunfeng Guo {
5564bec2d0bSJunfeng Guo 	struct gve_priv *hw = dev->data->dev_private;
5574bec2d0bSJunfeng Guo 	const struct rte_memzone *mz;
5584bec2d0bSJunfeng Guo 	struct gve_tx_queue *txq;
5594bec2d0bSJunfeng Guo 	uint16_t free_thresh;
5604bec2d0bSJunfeng Guo 	int err = 0;
5614bec2d0bSJunfeng Guo 
562cde01d16SJoshua Washington 	/* Ring size is required to be a power of two. */
563cde01d16SJoshua Washington 	if (!rte_is_power_of_2(nb_desc)) {
564*f665790aSDavid Marchand 		PMD_DRV_LOG(ERR, "Invalid ring size %u. GVE ring size must be a power of 2.",
565cde01d16SJoshua Washington 			    nb_desc);
566cde01d16SJoshua Washington 		return -EINVAL;
5674bec2d0bSJunfeng Guo 	}
5684bec2d0bSJunfeng Guo 
5694bec2d0bSJunfeng Guo 	/* Free memory if needed. */
5704bec2d0bSJunfeng Guo 	if (dev->data->tx_queues[queue_id]) {
57110d9e91aSJunfeng Guo 		gve_tx_queue_release(dev, queue_id);
5724bec2d0bSJunfeng Guo 		dev->data->tx_queues[queue_id] = NULL;
5734bec2d0bSJunfeng Guo 	}
5744bec2d0bSJunfeng Guo 
5754bec2d0bSJunfeng Guo 	/* Allocate the TX queue data structure. */
5764bec2d0bSJunfeng Guo 	txq = rte_zmalloc_socket("gve txq", sizeof(struct gve_tx_queue),
5774bec2d0bSJunfeng Guo 				 RTE_CACHE_LINE_SIZE, socket_id);
5784bec2d0bSJunfeng Guo 	if (!txq) {
5794bec2d0bSJunfeng Guo 		PMD_DRV_LOG(ERR, "Failed to allocate memory for tx queue structure");
5804bec2d0bSJunfeng Guo 		err = -ENOMEM;
5814bec2d0bSJunfeng Guo 		goto err_txq;
5824bec2d0bSJunfeng Guo 	}
5834bec2d0bSJunfeng Guo 
5844bec2d0bSJunfeng Guo 	free_thresh = conf->tx_free_thresh ? conf->tx_free_thresh : GVE_DEFAULT_TX_FREE_THRESH;
5854bec2d0bSJunfeng Guo 	if (free_thresh >= nb_desc - 3) {
5864bec2d0bSJunfeng Guo 		PMD_DRV_LOG(ERR, "tx_free_thresh (%u) must be less than nb_desc (%u) minus 3.",
5874bec2d0bSJunfeng Guo 			    free_thresh, txq->nb_tx_desc);
5884bec2d0bSJunfeng Guo 		err = -EINVAL;
5894bec2d0bSJunfeng Guo 		goto err_txq;
5904bec2d0bSJunfeng Guo 	}
5914bec2d0bSJunfeng Guo 
5924bec2d0bSJunfeng Guo 	txq->nb_tx_desc = nb_desc;
5934bec2d0bSJunfeng Guo 	txq->free_thresh = free_thresh;
5944bec2d0bSJunfeng Guo 	txq->queue_id = queue_id;
5954bec2d0bSJunfeng Guo 	txq->port_id = dev->data->port_id;
5964bec2d0bSJunfeng Guo 	txq->ntfy_id = queue_id;
5974bec2d0bSJunfeng Guo 	txq->is_gqi_qpl = hw->queue_format == GVE_GQI_QPL_FORMAT;
5984bec2d0bSJunfeng Guo 	txq->hw = hw;
5994bec2d0bSJunfeng Guo 	txq->ntfy_addr = &hw->db_bar2[rte_be_to_cpu_32(hw->irq_dbs[txq->ntfy_id].id)];
6004bec2d0bSJunfeng Guo 
6014bec2d0bSJunfeng Guo 	/* Allocate software ring */
6024bec2d0bSJunfeng Guo 	txq->sw_ring = rte_zmalloc_socket("gve tx sw ring",
6034bec2d0bSJunfeng Guo 					  sizeof(struct rte_mbuf *) * nb_desc,
6044bec2d0bSJunfeng Guo 					  RTE_CACHE_LINE_SIZE, socket_id);
6054bec2d0bSJunfeng Guo 	if (!txq->sw_ring) {
6064bec2d0bSJunfeng Guo 		PMD_DRV_LOG(ERR, "Failed to allocate memory for SW TX ring");
6074bec2d0bSJunfeng Guo 		err = -ENOMEM;
6084bec2d0bSJunfeng Guo 		goto err_txq;
6094bec2d0bSJunfeng Guo 	}
6104bec2d0bSJunfeng Guo 
6114bec2d0bSJunfeng Guo 	mz = rte_eth_dma_zone_reserve(dev, "tx_ring", queue_id,
6124bec2d0bSJunfeng Guo 				      nb_desc * sizeof(union gve_tx_desc),
6134bec2d0bSJunfeng Guo 				      PAGE_SIZE, socket_id);
6144bec2d0bSJunfeng Guo 	if (mz == NULL) {
6154bec2d0bSJunfeng Guo 		PMD_DRV_LOG(ERR, "Failed to reserve DMA memory for TX");
6164bec2d0bSJunfeng Guo 		err = -ENOMEM;
6174bec2d0bSJunfeng Guo 		goto err_sw_ring;
6184bec2d0bSJunfeng Guo 	}
6194bec2d0bSJunfeng Guo 	txq->tx_desc_ring = (union gve_tx_desc *)mz->addr;
6204bec2d0bSJunfeng Guo 	txq->tx_ring_phys_addr = mz->iova;
6214bec2d0bSJunfeng Guo 	txq->mz = mz;
6224bec2d0bSJunfeng Guo 
6237f369975SJoshua Washington 	/* QPL-specific allocations. */
6244bec2d0bSJunfeng Guo 	if (txq->is_gqi_qpl) {
6254bec2d0bSJunfeng Guo 		txq->iov_ring = rte_zmalloc_socket("gve tx iov ring",
6264bec2d0bSJunfeng Guo 						   sizeof(struct gve_tx_iovec) * nb_desc,
6274bec2d0bSJunfeng Guo 						   RTE_CACHE_LINE_SIZE, socket_id);
6284bec2d0bSJunfeng Guo 		if (!txq->iov_ring) {
6294bec2d0bSJunfeng Guo 			PMD_DRV_LOG(ERR, "Failed to allocate memory for SW TX ring");
6304bec2d0bSJunfeng Guo 			err = -ENOMEM;
6314bec2d0bSJunfeng Guo 			goto err_tx_ring;
6324bec2d0bSJunfeng Guo 		}
6337f369975SJoshua Washington 
6347f369975SJoshua Washington 		txq->qpl = gve_setup_queue_page_list(hw, queue_id, false,
6357f369975SJoshua Washington 						     hw->tx_pages_per_qpl);
6367f369975SJoshua Washington 		if (!txq->qpl) {
637cde01d16SJoshua Washington 			err = -ENOMEM;
6387f369975SJoshua Washington 			PMD_DRV_LOG(ERR, "Failed to alloc tx qpl for queue %hu.",
6397f369975SJoshua Washington 				    queue_id);
6404bec2d0bSJunfeng Guo 			goto err_iov_ring;
6414bec2d0bSJunfeng Guo 		}
6424bec2d0bSJunfeng Guo 	}
6434bec2d0bSJunfeng Guo 
6444bec2d0bSJunfeng Guo 	mz = rte_eth_dma_zone_reserve(dev, "txq_res", queue_id, sizeof(struct gve_queue_resources),
6454bec2d0bSJunfeng Guo 				      PAGE_SIZE, socket_id);
6464bec2d0bSJunfeng Guo 	if (mz == NULL) {
6474bec2d0bSJunfeng Guo 		PMD_DRV_LOG(ERR, "Failed to reserve DMA memory for TX resource");
6484bec2d0bSJunfeng Guo 		err = -ENOMEM;
6497f369975SJoshua Washington 		goto err_qpl;
6504bec2d0bSJunfeng Guo 	}
6514bec2d0bSJunfeng Guo 	txq->qres = (struct gve_queue_resources *)mz->addr;
6524bec2d0bSJunfeng Guo 	txq->qres_mz = mz;
6534bec2d0bSJunfeng Guo 
6544bec2d0bSJunfeng Guo 	gve_reset_txq(txq);
6554bec2d0bSJunfeng Guo 
6564bec2d0bSJunfeng Guo 	dev->data->tx_queues[queue_id] = txq;
6574bec2d0bSJunfeng Guo 
6584bec2d0bSJunfeng Guo 	return 0;
6597f369975SJoshua Washington err_qpl:
6607f369975SJoshua Washington 	if (txq->is_gqi_qpl) {
6617f369975SJoshua Washington 		gve_teardown_queue_page_list(hw, txq->qpl);
6627f369975SJoshua Washington 		txq->qpl = NULL;
6637f369975SJoshua Washington 	}
6644bec2d0bSJunfeng Guo err_iov_ring:
6654bec2d0bSJunfeng Guo 	if (txq->is_gqi_qpl)
6664bec2d0bSJunfeng Guo 		rte_free(txq->iov_ring);
6674bec2d0bSJunfeng Guo err_tx_ring:
6684bec2d0bSJunfeng Guo 	rte_memzone_free(txq->mz);
6694bec2d0bSJunfeng Guo err_sw_ring:
6704bec2d0bSJunfeng Guo 	rte_free(txq->sw_ring);
6714bec2d0bSJunfeng Guo err_txq:
6724bec2d0bSJunfeng Guo 	rte_free(txq);
6734bec2d0bSJunfeng Guo 	return err;
6744bec2d0bSJunfeng Guo }
6754bec2d0bSJunfeng Guo 
676b044845bSJunfeng Guo int
677b044845bSJunfeng Guo gve_tx_queue_start(struct rte_eth_dev *dev, uint16_t tx_queue_id)
678b044845bSJunfeng Guo {
679b044845bSJunfeng Guo 	struct gve_priv *hw = dev->data->dev_private;
680b044845bSJunfeng Guo 	struct gve_tx_queue *txq;
681b044845bSJunfeng Guo 
682b044845bSJunfeng Guo 	if (tx_queue_id >= dev->data->nb_tx_queues)
683b044845bSJunfeng Guo 		return -EINVAL;
684b044845bSJunfeng Guo 
685b044845bSJunfeng Guo 	txq = dev->data->tx_queues[tx_queue_id];
686b044845bSJunfeng Guo 
687b044845bSJunfeng Guo 	txq->qtx_tail = &hw->db_bar2[rte_be_to_cpu_32(txq->qres->db_index)];
688b044845bSJunfeng Guo 	txq->qtx_head =
689b044845bSJunfeng Guo 		&hw->cnt_array[rte_be_to_cpu_32(txq->qres->counter_index)];
690b044845bSJunfeng Guo 
691b044845bSJunfeng Guo 	rte_write32(rte_cpu_to_be_32(GVE_IRQ_MASK), txq->ntfy_addr);
692b044845bSJunfeng Guo 
6933caa72d3STathagat Priyadarshi 	dev->data->tx_queue_state[tx_queue_id] = RTE_ETH_QUEUE_STATE_STARTED;
694b044845bSJunfeng Guo 
695b044845bSJunfeng Guo 	return 0;
696b044845bSJunfeng Guo }
697b044845bSJunfeng Guo 
698b044845bSJunfeng Guo int
699b044845bSJunfeng Guo gve_tx_queue_stop(struct rte_eth_dev *dev, uint16_t tx_queue_id)
700b044845bSJunfeng Guo {
701b044845bSJunfeng Guo 	struct gve_tx_queue *txq;
702b044845bSJunfeng Guo 
703b044845bSJunfeng Guo 	if (tx_queue_id >= dev->data->nb_tx_queues)
704b044845bSJunfeng Guo 		return -EINVAL;
705b044845bSJunfeng Guo 
706b044845bSJunfeng Guo 	txq = dev->data->tx_queues[tx_queue_id];
707b044845bSJunfeng Guo 	gve_release_txq_mbufs(txq);
708b044845bSJunfeng Guo 	gve_reset_txq(txq);
709b044845bSJunfeng Guo 
710b044845bSJunfeng Guo 	dev->data->tx_queue_state[tx_queue_id] = RTE_ETH_QUEUE_STATE_STOPPED;
711b044845bSJunfeng Guo 
712b044845bSJunfeng Guo 	return 0;
713b044845bSJunfeng Guo }
714b044845bSJunfeng Guo 
7154bec2d0bSJunfeng Guo void
7164bec2d0bSJunfeng Guo gve_stop_tx_queues(struct rte_eth_dev *dev)
7174bec2d0bSJunfeng Guo {
7184bec2d0bSJunfeng Guo 	struct gve_priv *hw = dev->data->dev_private;
7194bec2d0bSJunfeng Guo 	uint16_t i;
7204bec2d0bSJunfeng Guo 	int err;
7214bec2d0bSJunfeng Guo 
7221e27182eSJunfeng Guo 	if (!gve_is_gqi(hw))
7231e27182eSJunfeng Guo 		return gve_stop_tx_queues_dqo(dev);
7241e27182eSJunfeng Guo 
7254bec2d0bSJunfeng Guo 	err = gve_adminq_destroy_tx_queues(hw, dev->data->nb_tx_queues);
7264bec2d0bSJunfeng Guo 	if (err != 0)
7274bec2d0bSJunfeng Guo 		PMD_DRV_LOG(WARNING, "failed to destroy txqs");
7284bec2d0bSJunfeng Guo 
729b044845bSJunfeng Guo 	for (i = 0; i < dev->data->nb_tx_queues; i++)
730b044845bSJunfeng Guo 		if (gve_tx_queue_stop(dev, i) != 0)
731b044845bSJunfeng Guo 			PMD_DRV_LOG(WARNING, "Fail to stop Tx queue %d", i);
7324bec2d0bSJunfeng Guo }
733b044845bSJunfeng Guo 
734b044845bSJunfeng Guo void
735b044845bSJunfeng Guo gve_set_tx_function(struct rte_eth_dev *dev)
736b044845bSJunfeng Guo {
737b044845bSJunfeng Guo 	dev->tx_pkt_burst = gve_tx_burst;
7384bec2d0bSJunfeng Guo }
739