Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2025 Intel Corporation
3 : : */
4 : :
5 : : #ifndef _COMMON_INTEL_TX_SCALAR_H_
6 : : #define _COMMON_INTEL_TX_SCALAR_H_
7 : :
8 : : #include <stdint.h>
9 : : #include <rte_io.h>
10 : : #include <rte_byteorder.h>
11 : :
12 : : /* depends on common Tx definitions. */
13 : : #include "tx.h"
14 : :
15 : : static inline void
16 : 0 : write_txd(volatile void *txd, uint64_t qw0, uint64_t qw1)
17 : : {
18 : : /* we use an aligned structure and cast away the volatile to allow the compiler
19 : : * to opportunistically optimize the two 64-bit writes as a single 128-bit write.
20 : : */
21 : : struct __rte_aligned(16) txdesc {
22 : : uint64_t qw0, qw1;
23 : : } *txdesc = RTE_CAST_PTR(struct txdesc *, txd);
24 : 0 : txdesc->qw0 = rte_cpu_to_le_64(qw0);
25 : 0 : txdesc->qw1 = rte_cpu_to_le_64(qw1);
26 : 0 : }
27 : :
28 : : static __rte_always_inline int
29 : : ci_tx_desc_done_simple(struct ci_tx_queue *txq, uint16_t idx)
30 : : {
31 [ # # # # ]: 0 : return (txq->ci_tx_ring[idx].cmd_type_offset_bsz & rte_cpu_to_le_64(CI_TXD_QW1_DTYPE_M)) ==
32 : : rte_cpu_to_le_64(CI_TX_DESC_DTYPE_DESC_DONE);
33 : : }
34 : :
35 : : /* Free transmitted mbufs using vector-style cleanup */
36 : : static __rte_always_inline int
37 : : ci_tx_free_bufs_simple(struct ci_tx_queue *txq)
38 : : {
39 : 0 : return ci_tx_free_bufs_vec(txq, ci_tx_desc_done_simple, false);
40 : : }
41 : :
42 : : /* Fill hardware descriptor ring with mbuf data (simple path) */
43 : : static inline void
44 : 0 : ci_tx_fill_hw_ring_simple(volatile struct ci_tx_desc *txdp, struct rte_mbuf **pkts,
45 : : uint16_t nb_pkts)
46 : : {
47 : : const int N_PER_LOOP = 4;
48 : : const int N_PER_LOOP_MASK = N_PER_LOOP - 1;
49 : : int mainpart, leftover;
50 : : int i, j;
51 : :
52 : 0 : mainpart = nb_pkts & ((uint32_t)~N_PER_LOOP_MASK);
53 : 0 : leftover = nb_pkts & ((uint32_t)N_PER_LOOP_MASK);
54 [ # # ]: 0 : for (i = 0; i < mainpart; i += N_PER_LOOP) {
55 [ # # ]: 0 : for (j = 0; j < N_PER_LOOP; ++j)
56 : 0 : write_txd(txdp + i + j, rte_mbuf_data_iova(*(pkts + i + j)),
57 : : CI_TX_DESC_DTYPE_DATA |
58 : : ((uint64_t)CI_TX_DESC_CMD_DEFAULT << CI_TXD_QW1_CMD_S) |
59 : 0 : ((uint64_t)(*(pkts + i + j))->data_len << CI_TXD_QW1_TX_BUF_SZ_S));
60 : : }
61 : :
62 [ # # ]: 0 : if (unlikely(leftover > 0)) {
63 [ # # ]: 0 : for (i = 0; i < leftover; ++i) {
64 : 0 : uint16_t idx = mainpart + i;
65 : 0 : write_txd(txdp + idx, rte_mbuf_data_iova(*(pkts + idx)),
66 : : CI_TX_DESC_DTYPE_DATA |
67 : : ((uint64_t)CI_TX_DESC_CMD_DEFAULT << CI_TXD_QW1_CMD_S) |
68 : 0 : ((uint64_t)(*(pkts + idx))->data_len << CI_TXD_QW1_TX_BUF_SZ_S));
69 : : }
70 : : }
71 : 0 : }
72 : :
73 : : /* Simple burst transmit for descriptor-based simple Tx path
74 : : *
75 : : * Transmits a burst of packets by filling hardware descriptors with mbuf
76 : : * data. Handles ring wrap-around and RS bit management. Performs descriptor
77 : : * cleanup when tx_free_thresh is reached.
78 : : *
79 : : * Returns: number of packets transmitted
80 : : */
81 : : static inline uint16_t
82 : 0 : ci_xmit_burst_simple(struct ci_tx_queue *txq,
83 : : struct rte_mbuf **tx_pkts,
84 : : uint16_t nb_pkts)
85 : : {
86 : 0 : volatile struct ci_tx_desc *txr = txq->ci_tx_ring;
87 : : volatile struct ci_tx_desc *txdp;
88 : : struct ci_tx_entry_vec *txep;
89 : : uint16_t tx_id;
90 : : uint16_t n = 0;
91 : :
92 : : /**
93 : : * Begin scanning the H/W ring for done descriptors when the number
94 : : * of available descriptors drops below tx_free_thresh. For each done
95 : : * descriptor, free the associated buffer.
96 : : */
97 [ # # ]: 0 : if (txq->nb_tx_free < txq->tx_free_thresh)
98 : : ci_tx_free_bufs_simple(txq);
99 : :
100 : : /* Use available descriptor only */
101 : 0 : nb_pkts = (uint16_t)RTE_MIN(txq->nb_tx_free, nb_pkts);
102 [ # # ]: 0 : if (unlikely(!nb_pkts))
103 : : return 0;
104 : :
105 : 0 : tx_id = txq->tx_tail;
106 : 0 : txdp = &txr[tx_id];
107 : 0 : txep = &txq->sw_ring_vec[tx_id];
108 : :
109 : 0 : txq->nb_tx_free = (uint16_t)(txq->nb_tx_free - nb_pkts);
110 : :
111 [ # # ]: 0 : if ((tx_id + nb_pkts) > txq->nb_tx_desc) {
112 : 0 : n = (uint16_t)(txq->nb_tx_desc - tx_id);
113 : :
114 : : /* Store mbufs in backlog */
115 : 0 : ci_tx_backlog_entry_vec(txep, tx_pkts, n);
116 : :
117 : : /* Write descriptors to HW ring */
118 : 0 : ci_tx_fill_hw_ring_simple(txdp, tx_pkts, n);
119 : :
120 : 0 : txr[txq->tx_next_rs].cmd_type_offset_bsz |=
121 : : rte_cpu_to_le_64(((uint64_t)CI_TX_DESC_CMD_RS) <<
122 : : CI_TXD_QW1_CMD_S);
123 : 0 : txq->tx_next_rs = (uint16_t)(txq->tx_rs_thresh - 1);
124 : :
125 : : tx_id = 0;
126 : : txdp = &txr[tx_id];
127 : : txep = &txq->sw_ring_vec[tx_id];
128 : : }
129 : :
130 : : /* Store remaining mbufs in backlog */
131 : 0 : ci_tx_backlog_entry_vec(txep, tx_pkts + n, (uint16_t)(nb_pkts - n));
132 : :
133 : : /* Write remaining descriptors to HW ring */
134 : 0 : ci_tx_fill_hw_ring_simple(txdp, tx_pkts + n, (uint16_t)(nb_pkts - n));
135 : :
136 : 0 : tx_id = (uint16_t)(tx_id + (nb_pkts - n));
137 : :
138 : : /* Determine if RS bit needs to be set */
139 [ # # ]: 0 : if (tx_id > txq->tx_next_rs) {
140 : 0 : txr[txq->tx_next_rs].cmd_type_offset_bsz |=
141 : : rte_cpu_to_le_64(((uint64_t)CI_TX_DESC_CMD_RS) <<
142 : : CI_TXD_QW1_CMD_S);
143 : 0 : txq->tx_next_rs =
144 : 0 : (uint16_t)(txq->tx_next_rs + txq->tx_rs_thresh);
145 [ # # ]: 0 : if (txq->tx_next_rs >= txq->nb_tx_desc)
146 : 0 : txq->tx_next_rs = (uint16_t)(txq->tx_rs_thresh - 1);
147 : : }
148 [ # # ]: 0 : if (tx_id == txq->nb_tx_desc)
149 : : tx_id = 0;
150 : :
151 : 0 : txq->tx_tail = tx_id;
152 : :
153 : : /* Update the tx tail register */
154 : 0 : rte_write32_wc((uint32_t)tx_id, txq->qtx_tail);
155 : :
156 : : return nb_pkts;
157 : : }
158 : :
159 : : static __rte_always_inline uint16_t
160 : : ci_xmit_pkts_simple(struct ci_tx_queue *txq,
161 : : struct rte_mbuf **tx_pkts,
162 : : uint16_t nb_pkts)
163 : : {
164 : : uint16_t nb_tx = 0;
165 : :
166 [ # # ]: 0 : if (likely(nb_pkts <= CI_TX_MAX_BURST))
167 : 0 : return ci_xmit_burst_simple(txq, tx_pkts, nb_pkts);
168 : :
169 [ # # ]: 0 : while (nb_pkts) {
170 : 0 : uint16_t ret, num = RTE_MIN(nb_pkts, CI_TX_MAX_BURST);
171 : :
172 : 0 : ret = ci_xmit_burst_simple(txq, &tx_pkts[nb_tx], num);
173 : 0 : nb_tx += ret;
174 : 0 : nb_pkts -= ret;
175 [ # # ]: 0 : if (ret < num)
176 : : break;
177 : : }
178 : :
179 : : return nb_tx;
180 : : }
181 : :
182 : : /*
183 : : * Common transmit descriptor cleanup function for Intel drivers.
184 : : *
185 : : * Returns:
186 : : * 0 on success
187 : : * -1 if cleanup cannot proceed (descriptors not yet processed by HW)
188 : : */
189 : : static __rte_always_inline int
190 : : ci_tx_xmit_cleanup(struct ci_tx_queue *txq)
191 : : {
192 : 0 : volatile struct ci_tx_desc *txd = txq->ci_tx_ring;
193 : 0 : const uint16_t last_desc_cleaned = txq->last_desc_cleaned;
194 : 0 : const uint16_t nb_tx_desc = txq->nb_tx_desc;
195 : :
196 : : /* Calculate where the next descriptor write-back will occur */
197 [ # # # # : 0 : const uint16_t rs_idx = (last_desc_cleaned == nb_tx_desc - 1) ?
# # # # #
# ]
198 : : 0 :
199 : 0 : (last_desc_cleaned + 1) >> txq->log2_rs_thresh;
200 : 0 : const uint16_t dd_idx = txq->rs_last_id[rs_idx];
201 : 0 : const uint16_t first_to_clean = rs_idx << txq->log2_rs_thresh;
202 : :
203 : : /* Check if descriptor is done - all drivers use 0xF as done value in bits 3:0 */
204 [ # # # # : 0 : if ((txd[dd_idx].cmd_type_offset_bsz & rte_cpu_to_le_64(CI_TXD_QW1_DTYPE_M)) !=
# # # # #
# ]
205 : : rte_cpu_to_le_64(CI_TX_DESC_DTYPE_DESC_DONE))
206 : : /* Descriptor not yet processed by hardware */
207 : : return -1;
208 : :
209 : : /* DD bit is set, descriptors are done. Now free the mbufs. */
210 : : /* Note: nb_tx_desc is guaranteed to be a multiple of tx_rs_thresh,
211 : : * validated during queue setup. This means cleanup never wraps around
212 : : * the ring within a single burst (e.g., ring=256, rs_thresh=32 gives
213 : : * bursts of 0-31, 32-63, ..., 224-255).
214 : : */
215 : 0 : const uint16_t nb_to_clean = txq->tx_rs_thresh;
216 : 0 : struct ci_tx_entry *sw_ring = txq->sw_ring;
217 : :
218 : : /* fast_free_mp is NULL only when the fast free is disabled*/
219 [ # # # # : 0 : if (txq->fast_free_mp != NULL) {
# # # # #
# ]
220 : : /* FAST_FREE path: mbufs are already reset, just return to pool */
221 : : struct rte_mbuf *free[CI_TX_MAX_FREE_BUF_SZ];
222 : : uint16_t nb_free = 0;
223 : :
224 : : /* Get cached mempool pointer, or cache it on first use.
225 : : * Read from mbuf at dd_idx, as it is guaranteed to be non-NULL.
226 : : */
227 : : struct rte_mempool *mp =
228 : 0 : likely(txq->fast_free_mp != (void *)UINTPTR_MAX) ?
229 [ # # # # : 0 : txq->fast_free_mp :
# # # # #
# ]
230 : 0 : (txq->fast_free_mp = sw_ring[dd_idx].mbuf->pool);
231 : :
232 : : /* Pack non-NULL mbufs in-place at start of sw_ring range.
233 : : * No modulo needed in loop since we're guaranteed not to wrap.
234 : : */
235 [ # # # # : 0 : for (uint16_t i = 0; i < nb_to_clean; i++) {
# # # # #
# ]
236 : 0 : struct rte_mbuf *m = sw_ring[first_to_clean + i].mbuf;
237 [ # # # # : 0 : if (m == NULL)
# # # # #
# ]
238 : 0 : continue;
239 : 0 : free[nb_free++] = m;
240 [ # # # # : 0 : if (unlikely(nb_free == CI_TX_MAX_FREE_BUF_SZ)) {
# # # # #
# ]
241 [ # # # # : 0 : rte_mbuf_raw_free_bulk(mp, free, nb_free);
# # # # #
# ]
242 : : nb_free = 0;
243 : : }
244 : : }
245 : :
246 : : /* Bulk return to mempool using packed sw_ring entries directly */
247 [ # # # # : 0 : if (nb_free > 0)
# # # # #
# ]
248 [ # # # # : 0 : rte_mbuf_raw_free_bulk(mp, free, nb_free);
# # # # #
# ]
249 : : } else {
250 : : /* Non-FAST_FREE path: use free_seg for refcount checks and freeing */
251 [ # # # # : 0 : for (uint16_t i = 0; i < nb_to_clean; i++) {
# # # # #
# ]
252 : 0 : struct rte_mbuf *m = sw_ring[first_to_clean + i].mbuf;
253 [ # # # # : 0 : if (m != NULL)
# # # # #
# ]
254 : : rte_pktmbuf_free_seg(m);
255 : : }
256 : : }
257 : :
258 : : /* Update the txq to reflect the last descriptor that was cleaned */
259 : 0 : txq->last_desc_cleaned = first_to_clean + txq->tx_rs_thresh - 1;
260 : 0 : txq->nb_tx_free += txq->tx_rs_thresh;
261 : :
262 : 0 : return 0;
263 : : }
264 : :
265 : : /* Common checksum enable function for Intel drivers (ice, i40e, etc.) */
266 : : static inline void
267 : 0 : ci_txd_enable_checksum(uint64_t ol_flags,
268 : : uint32_t *td_cmd,
269 : : uint32_t *td_offset,
270 : : union ci_tx_offload tx_offload)
271 : : {
272 : : /* Enable L3 checksum offloads */
273 [ # # ]: 0 : if (ol_flags & RTE_MBUF_F_TX_IP_CKSUM) {
274 : 0 : *td_cmd |= CI_TX_DESC_CMD_IIPT_IPV4_CSUM;
275 : 0 : *td_offset |= (tx_offload.l3_len >> 2) << CI_TX_DESC_LEN_IPLEN_S;
276 [ # # ]: 0 : } else if (ol_flags & RTE_MBUF_F_TX_IPV4) {
277 : 0 : *td_cmd |= CI_TX_DESC_CMD_IIPT_IPV4;
278 : 0 : *td_offset |= (tx_offload.l3_len >> 2) << CI_TX_DESC_LEN_IPLEN_S;
279 [ # # ]: 0 : } else if (ol_flags & RTE_MBUF_F_TX_IPV6) {
280 : 0 : *td_cmd |= CI_TX_DESC_CMD_IIPT_IPV6;
281 : 0 : *td_offset |= (tx_offload.l3_len >> 2) << CI_TX_DESC_LEN_IPLEN_S;
282 : : }
283 : :
284 [ # # ]: 0 : if (ol_flags & RTE_MBUF_F_TX_TCP_SEG) {
285 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_TCP;
286 : 0 : *td_offset |= (tx_offload.l4_len >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
287 : 0 : return;
288 : : }
289 : :
290 [ # # ]: 0 : if (ol_flags & RTE_MBUF_F_TX_UDP_SEG) {
291 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_UDP;
292 : 0 : *td_offset |= (tx_offload.l4_len >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
293 : 0 : return;
294 : : }
295 : :
296 : : /* Enable L4 checksum offloads */
297 [ # # # # ]: 0 : switch (ol_flags & RTE_MBUF_F_TX_L4_MASK) {
298 : 0 : case RTE_MBUF_F_TX_TCP_CKSUM:
299 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_TCP;
300 : 0 : *td_offset |= (sizeof(struct rte_tcp_hdr) >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
301 : 0 : break;
302 : 0 : case RTE_MBUF_F_TX_SCTP_CKSUM:
303 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_SCTP;
304 : 0 : *td_offset |= (sizeof(struct rte_sctp_hdr) >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
305 : 0 : break;
306 : 0 : case RTE_MBUF_F_TX_UDP_CKSUM:
307 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_UDP;
308 : 0 : *td_offset |= (sizeof(struct rte_udp_hdr) >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
309 : 0 : break;
310 : : default:
311 : : break;
312 : : }
313 : : }
314 : :
315 : : static inline uint16_t
316 : : ci_div_roundup16(uint16_t x, uint16_t y)
317 : : {
318 : 0 : return (uint16_t)((x + y - 1) / y);
319 : : }
320 : :
321 : : /* Calculate the number of TX descriptors needed for each pkt */
322 : : static inline uint16_t
323 : 0 : ci_calc_pkt_desc(const struct rte_mbuf *tx_pkt)
324 : : {
325 : : uint16_t count = 0;
326 : :
327 [ # # # # ]: 0 : while (tx_pkt != NULL) {
328 : 0 : count += ci_div_roundup16(tx_pkt->data_len, CI_MAX_DATA_PER_TXD);
329 : 0 : tx_pkt = tx_pkt->next;
330 : : }
331 : :
332 : 0 : return count;
333 : : }
334 : :
335 : : typedef uint16_t (*ci_get_ctx_desc_fn)(uint64_t ol_flags, const struct rte_mbuf *mbuf,
336 : : const union ci_tx_offload *tx_offload, const struct ci_tx_queue *txq,
337 : : uint64_t *qw0, uint64_t *qw1);
338 : :
339 : : /* gets IPsec descriptor information and returns number of descriptors needed (0 or 1) */
340 : : typedef uint16_t (*get_ipsec_desc_t)(const struct rte_mbuf *mbuf,
341 : : const struct ci_tx_queue *txq,
342 : : void **ipsec_metadata,
343 : : uint64_t *qw0,
344 : : uint64_t *qw1);
345 : : /* calculates segment length for IPsec + TSO combinations */
346 : : typedef uint16_t (*calc_ipsec_segment_len_t)(const struct rte_mbuf *mb_seg,
347 : : uint64_t ol_flags,
348 : : const void *ipsec_metadata,
349 : : uint16_t tlen);
350 : :
351 : : /** IPsec descriptor operations for drivers that support inline IPsec crypto. */
352 : : struct ci_ipsec_ops {
353 : : get_ipsec_desc_t get_ipsec_desc;
354 : : calc_ipsec_segment_len_t calc_segment_len;
355 : : };
356 : :
357 : : /* gets current timestamp tail index */
358 : : typedef uint16_t (*get_ts_tail_t)(struct ci_tx_queue *txq);
359 : : /* writes a timestamp descriptor and returns new tail index */
360 : : typedef uint16_t (*write_ts_desc_t)(struct ci_tx_queue *txq, struct rte_mbuf *mbuf,
361 : : uint16_t tx_id, uint16_t ts_id);
362 : : /* writes a timestamp tail index - doorbell */
363 : : typedef void (*write_ts_tail_t)(struct ci_tx_queue *txq, uint16_t ts_id);
364 : :
365 : : struct ci_timestamp_queue_fns {
366 : : get_ts_tail_t get_ts_tail;
367 : : write_ts_desc_t write_ts_desc;
368 : : write_ts_tail_t write_ts_tail;
369 : : };
370 : :
371 : : static inline uint16_t
372 : 0 : ci_xmit_pkts(struct ci_tx_queue *txq,
373 : : struct rte_mbuf **tx_pkts,
374 : : uint16_t nb_pkts,
375 : : enum ci_tx_l2tag1_field l2tag1_field,
376 : : ci_get_ctx_desc_fn get_ctx_desc,
377 : : const struct ci_ipsec_ops *ipsec_ops,
378 : : const struct ci_timestamp_queue_fns *ts_fns)
379 : : {
380 : : volatile struct ci_tx_desc *ci_tx_ring;
381 : : volatile struct ci_tx_desc *txd;
382 : : struct ci_tx_entry *sw_ring;
383 : : struct ci_tx_entry *txe, *txn;
384 : : struct rte_mbuf *tx_pkt;
385 : : struct rte_mbuf *m_seg;
386 : : uint16_t tx_id;
387 : : uint16_t ts_id = -1;
388 : : uint16_t nb_tx;
389 : : uint16_t nb_used;
390 : : uint16_t nb_ctx;
391 : 0 : uint32_t td_cmd = 0;
392 : 0 : uint32_t td_offset = 0;
393 : : uint32_t td_tag = 0;
394 : : uint16_t tx_last;
395 : : uint16_t slen;
396 : : uint16_t l2_len;
397 : : uint64_t buf_dma_addr;
398 : : uint64_t ol_flags;
399 : 0 : union ci_tx_offload tx_offload = {0};
400 : :
401 : 0 : sw_ring = txq->sw_ring;
402 : 0 : ci_tx_ring = txq->ci_tx_ring;
403 : 0 : tx_id = txq->tx_tail;
404 : 0 : txe = &sw_ring[tx_id];
405 : :
406 [ # # ]: 0 : if (ts_fns != NULL)
407 : 0 : ts_id = ts_fns->get_ts_tail(txq);
408 : :
409 : : /* Check if the descriptor ring needs to be cleaned. */
410 [ # # ]: 0 : if (txq->nb_tx_free < txq->tx_free_thresh)
411 : : (void)ci_tx_xmit_cleanup(txq);
412 : :
413 [ # # ]: 0 : for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
414 : 0 : void *ipsec_md = NULL;
415 : : uint16_t nb_ipsec = 0;
416 : 0 : uint64_t ipsec_qw0 = 0, ipsec_qw1 = 0;
417 : 0 : uint64_t cd_qw0 = 0, cd_qw1 = 0;
418 : : uint16_t pkt_rs_idx;
419 : 0 : tx_pkt = *tx_pkts++;
420 : :
421 : 0 : ol_flags = tx_pkt->ol_flags;
422 : 0 : td_cmd = CI_TX_DESC_CMD_ICRC;
423 : : td_tag = 0;
424 [ # # ]: 0 : l2_len = (ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK &&
425 [ # # ]: 0 : !(ol_flags & RTE_MBUF_F_TX_SEC_OFFLOAD)) ?
426 : 0 : tx_pkt->outer_l2_len : tx_pkt->l2_len;
427 : 0 : td_offset = (l2_len >> 1) << CI_TX_DESC_LEN_MACLEN_S;
428 : :
429 : :
430 : 0 : tx_offload.l2_len = tx_pkt->l2_len;
431 : 0 : tx_offload.l3_len = tx_pkt->l3_len;
432 : 0 : tx_offload.outer_l2_len = tx_pkt->outer_l2_len;
433 : 0 : tx_offload.outer_l3_len = tx_pkt->outer_l3_len;
434 : 0 : tx_offload.l4_len = tx_pkt->l4_len;
435 : 0 : tx_offload.tso_segsz = tx_pkt->tso_segsz;
436 : :
437 : : /* Calculate the number of context descriptors needed. */
438 : 0 : nb_ctx = get_ctx_desc(ol_flags, tx_pkt, &tx_offload, txq, &cd_qw0, &cd_qw1);
439 : :
440 : : /* Get IPsec descriptor information if IPsec ops provided */
441 [ # # ]: 0 : if (ipsec_ops != NULL)
442 : 0 : nb_ipsec = ipsec_ops->get_ipsec_desc(tx_pkt, txq, &ipsec_md,
443 : : &ipsec_qw0, &ipsec_qw1);
444 : :
445 : : /* The number of descriptors that must be allocated for
446 : : * a packet equals to the number of the segments of that
447 : : * packet plus the number of context and IPsec descriptors if needed.
448 : : * Recalculate the needed tx descs when TSO enabled in case
449 : : * the mbuf data size exceeds max data size that hw allows
450 : : * per tx desc.
451 : : */
452 [ # # ]: 0 : if (ol_flags & (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG))
453 : 0 : nb_used = (uint16_t)(ci_calc_pkt_desc(tx_pkt) + nb_ctx + nb_ipsec);
454 : : else
455 : 0 : nb_used = (uint16_t)(tx_pkt->nb_segs + nb_ctx + nb_ipsec);
456 : 0 : tx_last = (uint16_t)(tx_id + nb_used - 1);
457 : :
458 : : /* Circular ring */
459 [ # # ]: 0 : if (tx_last >= txq->nb_tx_desc)
460 : 0 : tx_last = (uint16_t)(tx_last - txq->nb_tx_desc);
461 : :
462 : : /* Track the RS threshold bucket at packet start */
463 : 0 : pkt_rs_idx = (uint16_t)(tx_id >> txq->log2_rs_thresh);
464 : :
465 [ # # ]: 0 : if (nb_used > txq->nb_tx_free) {
466 : : if (ci_tx_xmit_cleanup(txq) != 0) {
467 [ # # ]: 0 : if (nb_tx == 0)
468 : 0 : return 0;
469 : 0 : goto end_of_tx;
470 : : }
471 [ # # ]: 0 : if (unlikely(nb_used > txq->tx_rs_thresh)) {
472 [ # # ]: 0 : while (nb_used > txq->nb_tx_free) {
473 : : if (ci_tx_xmit_cleanup(txq) != 0) {
474 [ # # ]: 0 : if (nb_tx == 0)
475 : : return 0;
476 : 0 : goto end_of_tx;
477 : : }
478 : : }
479 : : }
480 : : }
481 : :
482 : : /* Descriptor based VLAN/QinQ insertion */
483 : : /* for single vlan offload, only insert in data desc with VLAN_IN_L2TAG1 is set
484 : : * for qinq offload, we always put inner tag in L2Tag1
485 : : */
486 [ # # # # ]: 0 : if (((ol_flags & RTE_MBUF_F_TX_VLAN) && l2tag1_field == CI_VLAN_IN_L2TAG1) ||
487 [ # # ]: 0 : (ol_flags & RTE_MBUF_F_TX_QINQ)) {
488 : 0 : td_cmd |= CI_TX_DESC_CMD_IL2TAG1;
489 : 0 : td_tag = tx_pkt->vlan_tci;
490 : : }
491 : :
492 : : /* Enable checksum offloading */
493 [ # # ]: 0 : if (ol_flags & CI_TX_CKSUM_OFFLOAD_MASK)
494 : 0 : ci_txd_enable_checksum(ol_flags, &td_cmd,
495 : : &td_offset, tx_offload);
496 : :
497 : : /* special case for single descriptor packet, without TSO offload */
498 [ # # # # ]: 0 : if (nb_used == 1 &&
499 : : (ol_flags & (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG)) == 0) {
500 : 0 : txd = &ci_tx_ring[tx_id];
501 : 0 : tx_id = txe->next_id;
502 : :
503 : 0 : txe->mbuf = tx_pkt;
504 : : /* Setup TX Descriptor */
505 : 0 : td_cmd |= CI_TX_DESC_CMD_EOP;
506 : 0 : const uint64_t cmd_type_offset_bsz = CI_TX_DESC_DTYPE_DATA |
507 : 0 : ((uint64_t)td_cmd << CI_TXD_QW1_CMD_S) |
508 : 0 : ((uint64_t)td_offset << CI_TXD_QW1_OFFSET_S) |
509 : 0 : ((uint64_t)tx_pkt->data_len << CI_TXD_QW1_TX_BUF_SZ_S) |
510 : 0 : ((uint64_t)td_tag << CI_TXD_QW1_L2TAG1_S);
511 : 0 : write_txd(txd, rte_mbuf_data_iova(tx_pkt), cmd_type_offset_bsz);
512 : :
513 : 0 : txe = &sw_ring[tx_id];
514 : 0 : goto end_pkt;
515 : : }
516 : :
517 [ # # ]: 0 : if (nb_ctx) {
518 : : /* Setup TX context descriptor if required */
519 : 0 : uint64_t *ctx_txd = RTE_CAST_PTR(uint64_t *, &ci_tx_ring[tx_id]);
520 : :
521 : 0 : txn = &sw_ring[txe->next_id];
522 : 0 : txe->mbuf = NULL;
523 : :
524 : 0 : write_txd(ctx_txd, cd_qw0, cd_qw1);
525 : :
526 : : tx_id = txe->next_id;
527 : : txe = txn;
528 : : }
529 : :
530 [ # # ]: 0 : if (ipsec_ops != NULL && nb_ipsec > 0) {
531 : : /* Setup TX IPsec descriptor if required */
532 : 0 : uint64_t *ipsec_txd = RTE_CAST_PTR(uint64_t *, &ci_tx_ring[tx_id]);
533 : :
534 : 0 : txn = &sw_ring[txe->next_id];
535 : 0 : txe->mbuf = NULL;
536 : :
537 : 0 : ipsec_txd[0] = ipsec_qw0;
538 : 0 : ipsec_txd[1] = ipsec_qw1;
539 : :
540 : : tx_id = txe->next_id;
541 : : txe = txn;
542 : : }
543 : :
544 : : m_seg = tx_pkt;
545 : :
546 : : do {
547 : 0 : txd = &ci_tx_ring[tx_id];
548 : 0 : txn = &sw_ring[txe->next_id];
549 : :
550 : : /* For FAST_FREE: reset mbuf fields while we have it in cache.
551 : : * [Fast free is indicated by txq->fast_free_mp being non-NULL.]
552 : : * FAST_FREE guarantees refcnt=1 and direct mbufs, so we only
553 : : * need to reset nb_segs and next pointer as per rte_pktmbuf_prefree_seg.
554 : : * Save next pointer before resetting since we need it for loop iteration.
555 : : */
556 : 0 : struct rte_mbuf *next_seg = m_seg->next;
557 [ # # ]: 0 : if (txq->fast_free_mp != NULL) {
558 [ # # ]: 0 : if (m_seg->nb_segs != 1)
559 : 0 : m_seg->nb_segs = 1;
560 [ # # ]: 0 : if (next_seg != NULL)
561 : 0 : m_seg->next = NULL;
562 : : }
563 : :
564 : : /* Setup TX Descriptor */
565 : : /* Calculate segment length, using IPsec callback if provided */
566 [ # # ]: 0 : if (ipsec_ops != NULL)
567 : 0 : slen = ipsec_ops->calc_segment_len(m_seg, ol_flags, ipsec_md, 0);
568 : : else
569 : 0 : slen = m_seg->data_len;
570 : :
571 : 0 : buf_dma_addr = rte_mbuf_data_iova(m_seg);
572 : :
573 [ # # ]: 0 : while ((ol_flags & (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG)) &&
574 [ # # ]: 0 : unlikely(slen > CI_MAX_DATA_PER_TXD)) {
575 : 0 : const uint64_t cmd_type_offset_bsz = CI_TX_DESC_DTYPE_DATA |
576 : 0 : ((uint64_t)td_cmd << CI_TXD_QW1_CMD_S) |
577 : 0 : ((uint64_t)td_offset << CI_TXD_QW1_OFFSET_S) |
578 : 0 : ((uint64_t)CI_MAX_DATA_PER_TXD << CI_TXD_QW1_TX_BUF_SZ_S) |
579 : 0 : ((uint64_t)td_tag << CI_TXD_QW1_L2TAG1_S);
580 : 0 : write_txd(txd, buf_dma_addr, cmd_type_offset_bsz);
581 : 0 : txe->mbuf = NULL;
582 : :
583 : 0 : buf_dma_addr += CI_MAX_DATA_PER_TXD;
584 : 0 : slen -= CI_MAX_DATA_PER_TXD;
585 : :
586 : 0 : tx_id = txe->next_id;
587 : : txe = txn;
588 : 0 : txd = &ci_tx_ring[tx_id];
589 : 0 : txn = &sw_ring[txe->next_id];
590 : : }
591 : :
592 : : /* fill the last descriptor with End of Packet (EOP) bit */
593 [ # # ]: 0 : if (next_seg == NULL)
594 : 0 : td_cmd |= CI_TX_DESC_CMD_EOP;
595 : :
596 : 0 : const uint64_t cmd_type_offset_bsz = CI_TX_DESC_DTYPE_DATA |
597 : 0 : ((uint64_t)td_cmd << CI_TXD_QW1_CMD_S) |
598 : 0 : ((uint64_t)td_offset << CI_TXD_QW1_OFFSET_S) |
599 : 0 : ((uint64_t)slen << CI_TXD_QW1_TX_BUF_SZ_S) |
600 : 0 : ((uint64_t)td_tag << CI_TXD_QW1_L2TAG1_S);
601 : 0 : write_txd(txd, buf_dma_addr, cmd_type_offset_bsz);
602 : 0 : txe->mbuf = m_seg;
603 : :
604 : 0 : tx_id = txe->next_id;
605 : : txe = txn;
606 : : m_seg = next_seg;
607 [ # # ]: 0 : } while (m_seg);
608 : 0 : end_pkt:
609 : 0 : txq->nb_tx_free = (uint16_t)(txq->nb_tx_free - nb_used);
610 : :
611 : : /* Check if packet crosses into a new RS threshold bucket.
612 : : * The RS bit is set on the last descriptor when we move from one bucket to another.
613 : : * For example, with tx_rs_thresh=32 and a 5-descriptor packet using slots 30-34:
614 : : * - pkt_rs_idx = 30 >> 5 = 0 (started in bucket 0)
615 : : * - tx_last = 34, so 35 >> 5 = 1 (next packet is in bucket 1)
616 : : * - Since 0 != 1, set RS bit on descriptor 34, and record rs_last_id[0] = 34
617 : : */
618 : 0 : uint16_t next_rs_idx = ((tx_last + 1) >> txq->log2_rs_thresh);
619 : :
620 [ # # ]: 0 : if (next_rs_idx != pkt_rs_idx) {
621 : : /* Packet crossed into a new bucket - set RS bit on last descriptor */
622 : 0 : txd->cmd_type_offset_bsz |=
623 : : rte_cpu_to_le_64(CI_TX_DESC_CMD_RS << CI_TXD_QW1_CMD_S);
624 : :
625 : : /* Record the last descriptor ID for the bucket we're leaving */
626 : 0 : txq->rs_last_id[pkt_rs_idx] = tx_last;
627 : : }
628 : :
629 [ # # ]: 0 : if (ts_fns != NULL)
630 : 0 : ts_id = ts_fns->write_ts_desc(txq, tx_pkt, tx_id, ts_id);
631 : : }
632 : 0 : end_of_tx:
633 : : /* update Tail register */
634 [ # # ]: 0 : if (ts_fns != NULL)
635 : 0 : ts_fns->write_ts_tail(txq, ts_id);
636 : : else
637 : 0 : rte_write32_wc(tx_id, txq->qtx_tail);
638 : 0 : txq->tx_tail = tx_id;
639 : :
640 : 0 : return nb_tx;
641 : : }
642 : :
643 : : #endif /* _COMMON_INTEL_TX_SCALAR_H_ */
|