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 : : 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 : : }
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 : uint16_t desc_to_clean_to = (rs_idx << txq->log2_rs_thresh) + (txq->tx_rs_thresh - 1);
201 : :
202 : : /* Check if descriptor is done */
203 [ # # # # : 0 : if ((txd[txq->rs_last_id[rs_idx]].cmd_type_offset_bsz &
# # # # #
# ]
204 : : rte_cpu_to_le_64(CI_TXD_QW1_DTYPE_M)) !=
205 : : rte_cpu_to_le_64(CI_TX_DESC_DTYPE_DESC_DONE))
206 : : return -1;
207 : :
208 : : /* Update the txq to reflect the last descriptor that was cleaned */
209 : 0 : txq->last_desc_cleaned = desc_to_clean_to;
210 : 0 : txq->nb_tx_free += txq->tx_rs_thresh;
211 : :
212 : 0 : return 0;
213 : : }
214 : :
215 : : /* Common checksum enable function for Intel drivers (ice, i40e, etc.) */
216 : : static inline void
217 : 0 : ci_txd_enable_checksum(uint64_t ol_flags,
218 : : uint32_t *td_cmd,
219 : : uint32_t *td_offset,
220 : : union ci_tx_offload tx_offload)
221 : : {
222 : : /* Enable L3 checksum offloads */
223 [ # # ]: 0 : if (ol_flags & RTE_MBUF_F_TX_IP_CKSUM) {
224 : 0 : *td_cmd |= CI_TX_DESC_CMD_IIPT_IPV4_CSUM;
225 : 0 : *td_offset |= (tx_offload.l3_len >> 2) << CI_TX_DESC_LEN_IPLEN_S;
226 [ # # ]: 0 : } else if (ol_flags & RTE_MBUF_F_TX_IPV4) {
227 : 0 : *td_cmd |= CI_TX_DESC_CMD_IIPT_IPV4;
228 : 0 : *td_offset |= (tx_offload.l3_len >> 2) << CI_TX_DESC_LEN_IPLEN_S;
229 [ # # ]: 0 : } else if (ol_flags & RTE_MBUF_F_TX_IPV6) {
230 : 0 : *td_cmd |= CI_TX_DESC_CMD_IIPT_IPV6;
231 : 0 : *td_offset |= (tx_offload.l3_len >> 2) << CI_TX_DESC_LEN_IPLEN_S;
232 : : }
233 : :
234 [ # # ]: 0 : if (ol_flags & RTE_MBUF_F_TX_TCP_SEG) {
235 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_TCP;
236 : 0 : *td_offset |= (tx_offload.l4_len >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
237 : 0 : return;
238 : : }
239 : :
240 [ # # ]: 0 : if (ol_flags & RTE_MBUF_F_TX_UDP_SEG) {
241 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_UDP;
242 : 0 : *td_offset |= (tx_offload.l4_len >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
243 : 0 : return;
244 : : }
245 : :
246 : : /* Enable L4 checksum offloads */
247 [ # # # # ]: 0 : switch (ol_flags & RTE_MBUF_F_TX_L4_MASK) {
248 : 0 : case RTE_MBUF_F_TX_TCP_CKSUM:
249 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_TCP;
250 : 0 : *td_offset |= (sizeof(struct rte_tcp_hdr) >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
251 : 0 : break;
252 : 0 : case RTE_MBUF_F_TX_SCTP_CKSUM:
253 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_SCTP;
254 : 0 : *td_offset |= (sizeof(struct rte_sctp_hdr) >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
255 : 0 : break;
256 : 0 : case RTE_MBUF_F_TX_UDP_CKSUM:
257 : 0 : *td_cmd |= CI_TX_DESC_CMD_L4T_EOFT_UDP;
258 : 0 : *td_offset |= (sizeof(struct rte_udp_hdr) >> 2) << CI_TX_DESC_LEN_L4_LEN_S;
259 : 0 : break;
260 : : default:
261 : : break;
262 : : }
263 : : }
264 : :
265 : : static inline uint16_t
266 : : ci_div_roundup16(uint16_t x, uint16_t y)
267 : : {
268 : 0 : return (uint16_t)((x + y - 1) / y);
269 : : }
270 : :
271 : : /* Calculate the number of TX descriptors needed for each pkt */
272 : : static inline uint16_t
273 : : ci_calc_pkt_desc(const struct rte_mbuf *tx_pkt)
274 : : {
275 : : uint16_t count = 0;
276 : :
277 [ # # # # ]: 0 : while (tx_pkt != NULL) {
278 : 0 : count += ci_div_roundup16(tx_pkt->data_len, CI_MAX_DATA_PER_TXD);
279 : 0 : tx_pkt = tx_pkt->next;
280 : : }
281 : :
282 : : return count;
283 : : }
284 : :
285 : : typedef uint16_t (*ci_get_ctx_desc_fn)(uint64_t ol_flags, const struct rte_mbuf *mbuf,
286 : : const union ci_tx_offload *tx_offload, const struct ci_tx_queue *txq,
287 : : uint64_t *qw0, uint64_t *qw1);
288 : :
289 : : /* gets IPsec descriptor information and returns number of descriptors needed (0 or 1) */
290 : : typedef uint16_t (*get_ipsec_desc_t)(const struct rte_mbuf *mbuf,
291 : : const struct ci_tx_queue *txq,
292 : : void **ipsec_metadata,
293 : : uint64_t *qw0,
294 : : uint64_t *qw1);
295 : : /* calculates segment length for IPsec + TSO combinations */
296 : : typedef uint16_t (*calc_ipsec_segment_len_t)(const struct rte_mbuf *mb_seg,
297 : : uint64_t ol_flags,
298 : : const void *ipsec_metadata,
299 : : uint16_t tlen);
300 : :
301 : : /** IPsec descriptor operations for drivers that support inline IPsec crypto. */
302 : : struct ci_ipsec_ops {
303 : : get_ipsec_desc_t get_ipsec_desc;
304 : : calc_ipsec_segment_len_t calc_segment_len;
305 : : };
306 : :
307 : : /* gets current timestamp tail index */
308 : : typedef uint16_t (*get_ts_tail_t)(struct ci_tx_queue *txq);
309 : : /* writes a timestamp descriptor and returns new tail index */
310 : : typedef uint16_t (*write_ts_desc_t)(struct ci_tx_queue *txq, struct rte_mbuf *mbuf,
311 : : uint16_t tx_id, uint16_t ts_id);
312 : : /* writes a timestamp tail index - doorbell */
313 : : typedef void (*write_ts_tail_t)(struct ci_tx_queue *txq, uint16_t ts_id);
314 : :
315 : : struct ci_timestamp_queue_fns {
316 : : get_ts_tail_t get_ts_tail;
317 : : write_ts_desc_t write_ts_desc;
318 : : write_ts_tail_t write_ts_tail;
319 : : };
320 : :
321 : : static inline uint16_t
322 : 0 : ci_xmit_pkts(struct ci_tx_queue *txq,
323 : : struct rte_mbuf **tx_pkts,
324 : : uint16_t nb_pkts,
325 : : enum ci_tx_l2tag1_field l2tag1_field,
326 : : ci_get_ctx_desc_fn get_ctx_desc,
327 : : const struct ci_ipsec_ops *ipsec_ops,
328 : : const struct ci_timestamp_queue_fns *ts_fns)
329 : : {
330 : : volatile struct ci_tx_desc *ci_tx_ring;
331 : : volatile struct ci_tx_desc *txd;
332 : : struct ci_tx_entry *sw_ring;
333 : : struct ci_tx_entry *txe, *txn;
334 : : struct rte_mbuf *tx_pkt;
335 : : struct rte_mbuf *m_seg;
336 : : uint16_t tx_id;
337 : : uint16_t ts_id = -1;
338 : : uint16_t nb_tx;
339 : : uint16_t nb_used;
340 : : uint16_t nb_ctx;
341 : 0 : uint32_t td_cmd = 0;
342 : 0 : uint32_t td_offset = 0;
343 : : uint32_t td_tag = 0;
344 : : uint16_t tx_last;
345 : : uint16_t slen;
346 : : uint16_t l2_len;
347 : : uint64_t buf_dma_addr;
348 : : uint64_t ol_flags;
349 : 0 : union ci_tx_offload tx_offload = {0};
350 : :
351 : 0 : sw_ring = txq->sw_ring;
352 : 0 : ci_tx_ring = txq->ci_tx_ring;
353 : 0 : tx_id = txq->tx_tail;
354 : 0 : txe = &sw_ring[tx_id];
355 : :
356 [ # # ]: 0 : if (ts_fns != NULL)
357 : 0 : ts_id = ts_fns->get_ts_tail(txq);
358 : :
359 : : /* Check if the descriptor ring needs to be cleaned. */
360 [ # # ]: 0 : if (txq->nb_tx_free < txq->tx_free_thresh)
361 : : (void)ci_tx_xmit_cleanup(txq);
362 : :
363 [ # # ]: 0 : for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
364 : 0 : void *ipsec_md = NULL;
365 : : uint16_t nb_ipsec = 0;
366 : 0 : uint64_t ipsec_qw0 = 0, ipsec_qw1 = 0;
367 : 0 : uint64_t cd_qw0 = 0, cd_qw1 = 0;
368 : : uint16_t pkt_rs_idx;
369 : 0 : tx_pkt = *tx_pkts++;
370 : :
371 : 0 : ol_flags = tx_pkt->ol_flags;
372 : 0 : td_cmd = CI_TX_DESC_CMD_ICRC;
373 : : td_tag = 0;
374 [ # # ]: 0 : l2_len = (ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK &&
375 [ # # ]: 0 : !(ol_flags & RTE_MBUF_F_TX_SEC_OFFLOAD)) ?
376 : 0 : tx_pkt->outer_l2_len : tx_pkt->l2_len;
377 : 0 : td_offset = (l2_len >> 1) << CI_TX_DESC_LEN_MACLEN_S;
378 : :
379 : :
380 : 0 : tx_offload.l2_len = tx_pkt->l2_len;
381 : 0 : tx_offload.l3_len = tx_pkt->l3_len;
382 : 0 : tx_offload.outer_l2_len = tx_pkt->outer_l2_len;
383 : 0 : tx_offload.outer_l3_len = tx_pkt->outer_l3_len;
384 : 0 : tx_offload.l4_len = tx_pkt->l4_len;
385 : 0 : tx_offload.tso_segsz = tx_pkt->tso_segsz;
386 : :
387 : : /* Calculate the number of context descriptors needed. */
388 : 0 : nb_ctx = get_ctx_desc(ol_flags, tx_pkt, &tx_offload, txq, &cd_qw0, &cd_qw1);
389 : :
390 : : /* Get IPsec descriptor information if IPsec ops provided */
391 [ # # ]: 0 : if (ipsec_ops != NULL)
392 : 0 : nb_ipsec = ipsec_ops->get_ipsec_desc(tx_pkt, txq, &ipsec_md,
393 : : &ipsec_qw0, &ipsec_qw1);
394 : :
395 : : /* The number of descriptors that must be allocated for
396 : : * a packet equals to the number of the segments of that
397 : : * packet plus the number of context and IPsec descriptors if needed.
398 : : * Recalculate the needed tx descs when TSO enabled in case
399 : : * the mbuf data size exceeds max data size that hw allows
400 : : * per tx desc.
401 : : */
402 [ # # ]: 0 : if (ol_flags & (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG))
403 : 0 : nb_used = (uint16_t)(ci_calc_pkt_desc(tx_pkt) + nb_ctx + nb_ipsec);
404 : : else
405 : 0 : nb_used = (uint16_t)(tx_pkt->nb_segs + nb_ctx + nb_ipsec);
406 : 0 : tx_last = (uint16_t)(tx_id + nb_used - 1);
407 : :
408 : : /* Circular ring */
409 [ # # ]: 0 : if (tx_last >= txq->nb_tx_desc)
410 : 0 : tx_last = (uint16_t)(tx_last - txq->nb_tx_desc);
411 : :
412 : : /* Track the RS threshold bucket at packet start */
413 : 0 : pkt_rs_idx = (uint16_t)(tx_id >> txq->log2_rs_thresh);
414 : :
415 [ # # ]: 0 : if (nb_used > txq->nb_tx_free) {
416 : : if (ci_tx_xmit_cleanup(txq) != 0) {
417 [ # # ]: 0 : if (nb_tx == 0)
418 : 0 : return 0;
419 : 0 : goto end_of_tx;
420 : : }
421 [ # # ]: 0 : if (unlikely(nb_used > txq->tx_rs_thresh)) {
422 [ # # ]: 0 : while (nb_used > txq->nb_tx_free) {
423 : : if (ci_tx_xmit_cleanup(txq) != 0) {
424 [ # # ]: 0 : if (nb_tx == 0)
425 : : return 0;
426 : 0 : goto end_of_tx;
427 : : }
428 : : }
429 : : }
430 : : }
431 : :
432 : : /* Descriptor based VLAN/QinQ insertion */
433 : : /* for single vlan offload, only insert in data desc with VLAN_IN_L2TAG1 is set
434 : : * for qinq offload, we always put inner tag in L2Tag1
435 : : */
436 [ # # # # ]: 0 : if (((ol_flags & RTE_MBUF_F_TX_VLAN) && l2tag1_field == CI_VLAN_IN_L2TAG1) ||
437 [ # # ]: 0 : (ol_flags & RTE_MBUF_F_TX_QINQ)) {
438 : 0 : td_cmd |= CI_TX_DESC_CMD_IL2TAG1;
439 : 0 : td_tag = tx_pkt->vlan_tci;
440 : : }
441 : :
442 : : /* Enable checksum offloading */
443 [ # # ]: 0 : if (ol_flags & CI_TX_CKSUM_OFFLOAD_MASK)
444 : 0 : ci_txd_enable_checksum(ol_flags, &td_cmd,
445 : : &td_offset, tx_offload);
446 : :
447 : : /* special case for single descriptor packet, without TSO offload */
448 [ # # # # ]: 0 : if (nb_used == 1 &&
449 : : (ol_flags & (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG)) == 0) {
450 : 0 : txd = &ci_tx_ring[tx_id];
451 : 0 : tx_id = txe->next_id;
452 : :
453 [ # # ]: 0 : if (txe->mbuf)
454 : : rte_pktmbuf_free_seg(txe->mbuf);
455 : 0 : txe->mbuf = tx_pkt;
456 : : /* Setup TX Descriptor */
457 : 0 : td_cmd |= CI_TX_DESC_CMD_EOP;
458 : 0 : const uint64_t cmd_type_offset_bsz = CI_TX_DESC_DTYPE_DATA |
459 : 0 : ((uint64_t)td_cmd << CI_TXD_QW1_CMD_S) |
460 : 0 : ((uint64_t)td_offset << CI_TXD_QW1_OFFSET_S) |
461 : 0 : ((uint64_t)tx_pkt->data_len << CI_TXD_QW1_TX_BUF_SZ_S) |
462 : 0 : ((uint64_t)td_tag << CI_TXD_QW1_L2TAG1_S);
463 : : write_txd(txd, rte_mbuf_data_iova(tx_pkt), cmd_type_offset_bsz);
464 : :
465 : 0 : txe = &sw_ring[tx_id];
466 : 0 : goto end_pkt;
467 : : }
468 : :
469 [ # # ]: 0 : if (nb_ctx) {
470 : : /* Setup TX context descriptor if required */
471 : 0 : uint64_t *ctx_txd = RTE_CAST_PTR(uint64_t *, &ci_tx_ring[tx_id]);
472 : :
473 : 0 : txn = &sw_ring[txe->next_id];
474 [ # # ]: 0 : RTE_MBUF_PREFETCH_TO_FREE(txn->mbuf);
475 [ # # ]: 0 : if (txe->mbuf) {
476 : : rte_pktmbuf_free_seg(txe->mbuf);
477 : 0 : txe->mbuf = NULL;
478 : : }
479 : :
480 : 0 : write_txd(ctx_txd, cd_qw0, cd_qw1);
481 : :
482 : 0 : tx_id = txe->next_id;
483 : : txe = txn;
484 : : }
485 : :
486 [ # # ]: 0 : if (ipsec_ops != NULL && nb_ipsec > 0) {
487 : : /* Setup TX IPsec descriptor if required */
488 : 0 : uint64_t *ipsec_txd = RTE_CAST_PTR(uint64_t *, &ci_tx_ring[tx_id]);
489 : :
490 : 0 : txn = &sw_ring[txe->next_id];
491 [ # # ]: 0 : RTE_MBUF_PREFETCH_TO_FREE(txn->mbuf);
492 [ # # ]: 0 : if (txe->mbuf) {
493 : : rte_pktmbuf_free_seg(txe->mbuf);
494 : 0 : txe->mbuf = NULL;
495 : : }
496 : :
497 : 0 : ipsec_txd[0] = ipsec_qw0;
498 : 0 : ipsec_txd[1] = ipsec_qw1;
499 : :
500 : 0 : tx_id = txe->next_id;
501 : : txe = txn;
502 : : }
503 : :
504 : : m_seg = tx_pkt;
505 : :
506 : : do {
507 : 0 : txd = &ci_tx_ring[tx_id];
508 : 0 : txn = &sw_ring[txe->next_id];
509 : :
510 [ # # ]: 0 : if (txe->mbuf)
511 : : rte_pktmbuf_free_seg(txe->mbuf);
512 : 0 : txe->mbuf = m_seg;
513 : :
514 : : /* Setup TX Descriptor */
515 : : /* Calculate segment length, using IPsec callback if provided */
516 [ # # ]: 0 : if (ipsec_ops != NULL)
517 : 0 : slen = ipsec_ops->calc_segment_len(m_seg, ol_flags, ipsec_md, 0);
518 : : else
519 : 0 : slen = m_seg->data_len;
520 : :
521 : : buf_dma_addr = rte_mbuf_data_iova(m_seg);
522 : :
523 [ # # ]: 0 : while ((ol_flags & (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG)) &&
524 [ # # ]: 0 : unlikely(slen > CI_MAX_DATA_PER_TXD)) {
525 : 0 : const uint64_t cmd_type_offset_bsz = CI_TX_DESC_DTYPE_DATA |
526 : 0 : ((uint64_t)td_cmd << CI_TXD_QW1_CMD_S) |
527 : 0 : ((uint64_t)td_offset << CI_TXD_QW1_OFFSET_S) |
528 : 0 : ((uint64_t)CI_MAX_DATA_PER_TXD << CI_TXD_QW1_TX_BUF_SZ_S) |
529 : 0 : ((uint64_t)td_tag << CI_TXD_QW1_L2TAG1_S);
530 : : write_txd(txd, buf_dma_addr, cmd_type_offset_bsz);
531 : :
532 : 0 : buf_dma_addr += CI_MAX_DATA_PER_TXD;
533 : 0 : slen -= CI_MAX_DATA_PER_TXD;
534 : :
535 : 0 : tx_id = txe->next_id;
536 : : txe = txn;
537 : 0 : txd = &ci_tx_ring[tx_id];
538 : 0 : txn = &sw_ring[txe->next_id];
539 : : }
540 : :
541 : : /* fill the last descriptor with End of Packet (EOP) bit */
542 [ # # ]: 0 : if (m_seg->next == NULL)
543 : 0 : td_cmd |= CI_TX_DESC_CMD_EOP;
544 : :
545 : 0 : const uint64_t cmd_type_offset_bsz = CI_TX_DESC_DTYPE_DATA |
546 : 0 : ((uint64_t)td_cmd << CI_TXD_QW1_CMD_S) |
547 : 0 : ((uint64_t)td_offset << CI_TXD_QW1_OFFSET_S) |
548 : 0 : ((uint64_t)slen << CI_TXD_QW1_TX_BUF_SZ_S) |
549 : 0 : ((uint64_t)td_tag << CI_TXD_QW1_L2TAG1_S);
550 : : write_txd(txd, buf_dma_addr, cmd_type_offset_bsz);
551 : :
552 : 0 : tx_id = txe->next_id;
553 : : txe = txn;
554 : : m_seg = m_seg->next;
555 [ # # ]: 0 : } while (m_seg);
556 : 0 : end_pkt:
557 : 0 : txq->nb_tx_free = (uint16_t)(txq->nb_tx_free - nb_used);
558 : :
559 : : /* Check if packet crosses into a new RS threshold bucket.
560 : : * The RS bit is set on the last descriptor when we move from one bucket to another.
561 : : * For example, with tx_rs_thresh=32 and a 5-descriptor packet using slots 30-34:
562 : : * - pkt_rs_idx = 30 >> 5 = 0 (started in bucket 0)
563 : : * - tx_last = 34, so 35 >> 5 = 1 (next packet is in bucket 1)
564 : : * - Since 0 != 1, set RS bit on descriptor 34, and record rs_last_id[0] = 34
565 : : */
566 : 0 : uint16_t next_rs_idx = ((tx_last + 1) >> txq->log2_rs_thresh);
567 : :
568 [ # # ]: 0 : if (next_rs_idx != pkt_rs_idx) {
569 : : /* Packet crossed into a new bucket - set RS bit on last descriptor */
570 : 0 : txd->cmd_type_offset_bsz |=
571 : : rte_cpu_to_le_64(CI_TX_DESC_CMD_RS << CI_TXD_QW1_CMD_S);
572 : :
573 : : /* Record the last descriptor ID for the bucket we're leaving */
574 : 0 : txq->rs_last_id[pkt_rs_idx] = tx_last;
575 : : }
576 : :
577 [ # # ]: 0 : if (ts_fns != NULL)
578 : 0 : ts_id = ts_fns->write_ts_desc(txq, tx_pkt, tx_id, ts_id);
579 : : }
580 : 0 : end_of_tx:
581 : : /* update Tail register */
582 [ # # ]: 0 : if (ts_fns != NULL)
583 : 0 : ts_fns->write_ts_tail(txq, ts_id);
584 : : else
585 : 0 : rte_write32_wc(tx_id, txq->qtx_tail);
586 : 0 : txq->tx_tail = tx_id;
587 : :
588 : 0 : return nb_tx;
589 : : }
590 : :
591 : : #endif /* _COMMON_INTEL_TX_SCALAR_H_ */
|