Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright 2021 6WIND S.A.
3 : : * Copyright 2021 Mellanox Technologies, Ltd
4 : : */
5 : :
6 : : #ifndef RTE_PMD_MLX5_TX_H_
7 : : #define RTE_PMD_MLX5_TX_H_
8 : :
9 : : #include <stdint.h>
10 : : #include <sys/queue.h>
11 : :
12 : : #include <rte_mbuf.h>
13 : : #include <rte_mempool.h>
14 : : #include <rte_common.h>
15 : : #include <rte_spinlock.h>
16 : : #include <rte_trace_point.h>
17 : :
18 : : #include <mlx5_common.h>
19 : : #include <mlx5_common_mr.h>
20 : :
21 : : #include "mlx5.h"
22 : : #include "mlx5_autoconf.h"
23 : : #include "mlx5_rxtx.h"
24 : : #include "mlx5_trace.h"
25 : :
26 : : /* TX burst subroutines return codes. */
27 : : enum mlx5_txcmp_code {
28 : : MLX5_TXCMP_CODE_EXIT = 0,
29 : : MLX5_TXCMP_CODE_ERROR,
30 : : MLX5_TXCMP_CODE_SINGLE,
31 : : MLX5_TXCMP_CODE_MULTI,
32 : : MLX5_TXCMP_CODE_TSO,
33 : : MLX5_TXCMP_CODE_EMPW,
34 : : };
35 : :
36 : : /*
37 : : * These defines are used to configure Tx burst routine option set supported
38 : : * at compile time. The not specified options are optimized out due to if
39 : : * conditions can be explicitly calculated at compile time.
40 : : * The offloads with bigger runtime check (require more CPU cycles toskip)
41 : : * overhead should have the bigger index - this is needed to select the better
42 : : * matching routine function if no exact match and some offloads are not
43 : : * actually requested.
44 : : */
45 : : #define MLX5_TXOFF_CONFIG_MULTI (1u << 0) /* Multi-segment packets.*/
46 : : #define MLX5_TXOFF_CONFIG_TSO (1u << 1) /* TCP send offload supported.*/
47 : : #define MLX5_TXOFF_CONFIG_SWP (1u << 2) /* Tunnels/SW Parser offloads.*/
48 : : #define MLX5_TXOFF_CONFIG_CSUM (1u << 3) /* Check Sums offloaded. */
49 : : #define MLX5_TXOFF_CONFIG_INLINE (1u << 4) /* Data inlining supported. */
50 : : #define MLX5_TXOFF_CONFIG_VLAN (1u << 5) /* VLAN insertion supported.*/
51 : : #define MLX5_TXOFF_CONFIG_METADATA (1u << 6) /* Flow metadata. */
52 : : #define MLX5_TXOFF_CONFIG_EMPW (1u << 8) /* Enhanced MPW supported.*/
53 : : #define MLX5_TXOFF_CONFIG_MPW (1u << 9) /* Legacy MPW supported.*/
54 : : #define MLX5_TXOFF_CONFIG_TXPP (1u << 10) /* Scheduling on timestamp.*/
55 : :
56 : : /* The most common offloads groups. */
57 : : #define MLX5_TXOFF_CONFIG_NONE 0
58 : : #define MLX5_TXOFF_CONFIG_FULL (MLX5_TXOFF_CONFIG_MULTI | \
59 : : MLX5_TXOFF_CONFIG_TSO | \
60 : : MLX5_TXOFF_CONFIG_SWP | \
61 : : MLX5_TXOFF_CONFIG_CSUM | \
62 : : MLX5_TXOFF_CONFIG_INLINE | \
63 : : MLX5_TXOFF_CONFIG_VLAN | \
64 : : MLX5_TXOFF_CONFIG_METADATA)
65 : :
66 : : #define MLX5_TXOFF_CONFIG(mask) (olx & MLX5_TXOFF_CONFIG_##mask)
67 : :
68 : : #define MLX5_TXOFF_PRE_DECL(func) \
69 : : uint16_t mlx5_tx_burst_##func(void *txq, \
70 : : struct rte_mbuf **pkts, \
71 : : uint16_t pkts_n)
72 : :
73 : : #define MLX5_TXOFF_DECL(func, olx) \
74 : : uint16_t mlx5_tx_burst_##func(void *txq, \
75 : : struct rte_mbuf **pkts, \
76 : : uint16_t pkts_n) \
77 : : { \
78 : : return mlx5_tx_burst_tmpl((struct mlx5_txq_data *)txq, \
79 : : pkts, pkts_n, (olx)); \
80 : : }
81 : :
82 : : /* Mbuf dynamic flag offset for inline. */
83 : : extern uint64_t rte_net_mlx5_dynf_inline_mask;
84 : : #define RTE_MBUF_F_TX_DYNF_NOINLINE rte_net_mlx5_dynf_inline_mask
85 : :
86 : : extern alignas(RTE_CACHE_LINE_SIZE) uint32_t mlx5_ptype_table[];
87 : : extern alignas(RTE_CACHE_LINE_SIZE) uint8_t mlx5_cksum_table[1 << 10];
88 : : extern alignas(RTE_CACHE_LINE_SIZE) uint8_t mlx5_swp_types_table[1 << 10];
89 : :
90 : : struct mlx5_txq_stats {
91 : : #ifdef MLX5_PMD_SOFT_COUNTERS
92 : : uint64_t opackets; /**< Total of successfully sent packets. */
93 : : uint64_t obytes; /**< Total of successfully sent bytes. */
94 : : #endif
95 : : uint64_t oerrors; /**< Total number of failed transmitted packets. */
96 : : };
97 : :
98 : : /* TX queue send local data. */
99 : : __extension__
100 : : struct mlx5_txq_local {
101 : : struct mlx5_wqe *wqe_last; /* last sent WQE pointer. */
102 : : struct rte_mbuf *mbuf; /* first mbuf to process. */
103 : : uint16_t pkts_copy; /* packets copied to elts. */
104 : : uint16_t pkts_sent; /* packets sent. */
105 : : uint16_t pkts_loop; /* packets sent on loop entry. */
106 : : uint16_t elts_free; /* available elts remain. */
107 : : uint16_t wqe_free; /* available wqe remain. */
108 : : uint16_t mbuf_off; /* data offset in current mbuf. */
109 : : uint16_t mbuf_nseg; /* number of remaining mbuf. */
110 : : uint16_t mbuf_free; /* number of inline mbufs to free. */
111 : : };
112 : :
113 : : /* TX queue descriptor. */
114 : : __extension__
115 : : struct __rte_cache_aligned mlx5_txq_data {
116 : : uint16_t elts_head; /* Current counter in (*elts)[]. */
117 : : uint16_t elts_tail; /* Counter of first element awaiting completion. */
118 : : uint16_t elts_comp; /* elts index since last completion request. */
119 : : uint16_t elts_s; /* Number of mbuf elements. */
120 : : uint16_t elts_m; /* Mask for mbuf elements indices. */
121 : : /* Fields related to elts mbuf storage. */
122 : : uint16_t wqe_ci; /* Consumer index for work queue. */
123 : : uint16_t wqe_pi; /* Producer index for work queue. */
124 : : uint16_t wqe_s; /* Number of WQ elements. */
125 : : uint16_t wqe_m; /* Mask Number for WQ elements. */
126 : : uint16_t wqe_comp; /* WQE index since last completion request. */
127 : : uint16_t wqe_thres; /* WQE threshold to request completion in CQ. */
128 : : /* WQ related fields. */
129 : : uint16_t cq_ci; /* Consumer index for completion queue. */
130 : : uint16_t cq_pi; /* Production index for completion queue. */
131 : : uint16_t cqe_s; /* Number of CQ elements. */
132 : : uint16_t cqe_m; /* Mask for CQ indices. */
133 : : /* CQ related fields. */
134 : : uint16_t elts_n:4; /* elts[] length (in log2). */
135 : : uint16_t cqe_n:4; /* Number of CQ elements (in log2). */
136 : : uint16_t wqe_n:4; /* Number of WQ elements (in log2). */
137 : : uint16_t tso_en:1; /* When set hardware TSO is enabled. */
138 : : uint16_t tunnel_en:1;
139 : : /* When set TX offload for tunneled packets are supported. */
140 : : uint16_t swp_en:1; /* Whether SW parser is enabled. */
141 : : uint16_t vlan_en:1; /* VLAN insertion in WQE is supported. */
142 : : uint16_t db_nc:1; /* Doorbell mapped to non-cached region. */
143 : : uint16_t db_heu:1; /* Doorbell heuristic write barrier. */
144 : : uint16_t rt_timestamp:1; /* Realtime timestamp format. */
145 : : uint16_t wait_on_time:1; /* WQE with timestamp is supported. */
146 : : uint16_t fast_free:1; /* mbuf fast free on Tx is enabled. */
147 : : uint16_t inlen_send; /* Ordinary send data inline size. */
148 : : uint16_t inlen_empw; /* eMPW max packet size to inline. */
149 : : uint16_t inlen_mode; /* Minimal data length to inline. */
150 : : uint8_t tx_aggr_affinity; /* TxQ affinity configuration. */
151 : : uint32_t qp_num_8s; /* QP number shifted by 8. */
152 : : uint32_t sq_mem_len; /* Length of TxQ for WQEs */
153 : : uint64_t offloads; /* Offloads for Tx Queue. */
154 : : struct mlx5_mr_ctrl mr_ctrl; /* MR control descriptor. */
155 : : struct mlx5_wqe *wqes; /* Work queue. */
156 : : struct mlx5_wqe *wqes_end; /* Work queue array limit. */
157 : : #ifdef RTE_LIBRTE_MLX5_DEBUG
158 : : uint32_t *fcqs; /* Free completion queue (debug extended). */
159 : : #else
160 : : uint16_t *fcqs; /* Free completion queue. */
161 : : #endif
162 : : volatile struct mlx5_cqe *cqes; /* Completion queue. */
163 : : volatile uint32_t *qp_db; /* Work queue doorbell. */
164 : : volatile uint32_t *cq_db; /* Completion queue doorbell. */
165 : : uint16_t port_id; /* Port ID of device. */
166 : : uint16_t idx; /* Queue index. */
167 : : uint64_t rt_timemask; /* Scheduling timestamp mask. */
168 : : uint64_t ts_mask; /* Timestamp flag dynamic mask. */
169 : : uint64_t ts_last; /* Last scheduled timestamp. */
170 : : int32_t ts_offset; /* Timestamp field dynamic offset. */
171 : : uint32_t cq_mem_len; /* Length of TxQ for CQEs */
172 : : struct mlx5_dev_ctx_shared *sh; /* Shared context. */
173 : : struct mlx5_txq_stats stats; /* TX queue counters. */
174 : : struct mlx5_txq_stats stats_reset; /* stats on last reset. */
175 : : struct mlx5_uar_data uar_data;
176 : : struct rte_mbuf *elts[];
177 : : /* Storage for queued packets, must be the last field. */
178 : : };
179 : :
180 : : /* TX queue control descriptor. */
181 : : __extension__
182 : : struct mlx5_txq_ctrl {
183 : : LIST_ENTRY(mlx5_txq_ctrl) next; /* Pointer to the next element. */
184 : : RTE_ATOMIC(uint32_t) refcnt; /* Reference counter. */
185 : : unsigned int socket; /* CPU socket ID for allocations. */
186 : : bool is_hairpin; /* Whether TxQ type is Hairpin. */
187 : : unsigned int max_inline_data; /* Max inline data. */
188 : : unsigned int max_tso_header; /* Max TSO header size. */
189 : : struct mlx5_txq_obj *obj; /* Verbs/DevX queue object. */
190 : : struct mlx5_priv *priv; /* Back pointer to private data. */
191 : : off_t uar_mmap_offset; /* UAR mmap offset for non-primary process. */
192 : : uint16_t dump_file_n; /* Number of dump files. */
193 : : struct rte_eth_hairpin_conf hairpin_conf; /* Hairpin configuration. */
194 : : uint32_t hairpin_status; /* Hairpin binding status. */
195 : : struct mlx5_txq_data txq; /* Data path structure. */
196 : : /* Must be the last field in the structure, contains elts[]. */
197 : : };
198 : :
199 : : /* mlx5_txq.c */
200 : :
201 : : int mlx5_tx_queue_start(struct rte_eth_dev *dev, uint16_t queue_id);
202 : : int mlx5_tx_queue_stop(struct rte_eth_dev *dev, uint16_t queue_id);
203 : : int mlx5_tx_queue_start_primary(struct rte_eth_dev *dev, uint16_t queue_id);
204 : : int mlx5_tx_queue_stop_primary(struct rte_eth_dev *dev, uint16_t queue_id);
205 : : int mlx5_tx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
206 : : unsigned int socket, const struct rte_eth_txconf *conf);
207 : : int mlx5_tx_hairpin_queue_setup
208 : : (struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
209 : : const struct rte_eth_hairpin_conf *hairpin_conf);
210 : : void mlx5_tx_queue_release(struct rte_eth_dev *dev, uint16_t qid);
211 : : int mlx5_tx_uar_init_secondary(struct rte_eth_dev *dev, int fd);
212 : : void mlx5_tx_uar_uninit_secondary(struct rte_eth_dev *dev);
213 : : int mlx5_txq_obj_verify(struct rte_eth_dev *dev);
214 : : struct mlx5_txq_ctrl *mlx5_txq_new(struct rte_eth_dev *dev, uint16_t idx,
215 : : uint16_t desc, unsigned int socket,
216 : : const struct rte_eth_txconf *conf);
217 : : struct mlx5_txq_ctrl *mlx5_txq_hairpin_new
218 : : (struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
219 : : const struct rte_eth_hairpin_conf *hairpin_conf);
220 : : struct mlx5_txq_ctrl *mlx5_txq_get(struct rte_eth_dev *dev, uint16_t idx);
221 : : int mlx5_txq_release(struct rte_eth_dev *dev, uint16_t idx);
222 : : int mlx5_txq_releasable(struct rte_eth_dev *dev, uint16_t idx);
223 : : int mlx5_txq_verify(struct rte_eth_dev *dev);
224 : : int mlx5_txq_get_sqn(struct mlx5_txq_ctrl *txq);
225 : : void txq_alloc_elts(struct mlx5_txq_ctrl *txq_ctrl);
226 : : void txq_free_elts(struct mlx5_txq_ctrl *txq_ctrl);
227 : : uint64_t mlx5_get_tx_port_offloads(struct rte_eth_dev *dev);
228 : : void mlx5_txq_dynf_timestamp_set(struct rte_eth_dev *dev);
229 : : int mlx5_count_aggr_ports(struct rte_eth_dev *dev);
230 : : int mlx5_map_aggr_tx_affinity(struct rte_eth_dev *dev, uint16_t tx_queue_id,
231 : : uint8_t affinity);
232 : : int mlx5_ext_txq_verify(struct rte_eth_dev *dev);
233 : : struct mlx5_external_q *mlx5_ext_txq_get(struct rte_eth_dev *dev, uint16_t idx);
234 : :
235 : : /* mlx5_tx.c */
236 : :
237 : : void mlx5_tx_handle_completion(struct mlx5_txq_data *__rte_restrict txq,
238 : : unsigned int olx __rte_unused);
239 : : int mlx5_tx_descriptor_status(void *tx_queue, uint16_t offset);
240 : : void mlx5_txq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,
241 : : struct rte_eth_txq_info *qinfo);
242 : : int mlx5_tx_burst_mode_get(struct rte_eth_dev *dev, uint16_t tx_queue_id,
243 : : struct rte_eth_burst_mode *mode);
244 : :
245 : : /* mlx5_tx_empw.c */
246 : :
247 : : MLX5_TXOFF_PRE_DECL(full_empw);
248 : : MLX5_TXOFF_PRE_DECL(none_empw);
249 : : MLX5_TXOFF_PRE_DECL(md_empw);
250 : : MLX5_TXOFF_PRE_DECL(mt_empw);
251 : : MLX5_TXOFF_PRE_DECL(mtsc_empw);
252 : : MLX5_TXOFF_PRE_DECL(mti_empw);
253 : : MLX5_TXOFF_PRE_DECL(mtv_empw);
254 : : MLX5_TXOFF_PRE_DECL(mtiv_empw);
255 : : MLX5_TXOFF_PRE_DECL(sc_empw);
256 : : MLX5_TXOFF_PRE_DECL(sci_empw);
257 : : MLX5_TXOFF_PRE_DECL(scv_empw);
258 : : MLX5_TXOFF_PRE_DECL(sciv_empw);
259 : : MLX5_TXOFF_PRE_DECL(i_empw);
260 : : MLX5_TXOFF_PRE_DECL(v_empw);
261 : : MLX5_TXOFF_PRE_DECL(iv_empw);
262 : :
263 : : /* mlx5_tx_nompw.c */
264 : :
265 : : MLX5_TXOFF_PRE_DECL(full);
266 : : MLX5_TXOFF_PRE_DECL(none);
267 : : MLX5_TXOFF_PRE_DECL(md);
268 : : MLX5_TXOFF_PRE_DECL(mt);
269 : : MLX5_TXOFF_PRE_DECL(mtsc);
270 : : MLX5_TXOFF_PRE_DECL(mti);
271 : : MLX5_TXOFF_PRE_DECL(mtv);
272 : : MLX5_TXOFF_PRE_DECL(mtiv);
273 : : MLX5_TXOFF_PRE_DECL(sc);
274 : : MLX5_TXOFF_PRE_DECL(sci);
275 : : MLX5_TXOFF_PRE_DECL(scv);
276 : : MLX5_TXOFF_PRE_DECL(sciv);
277 : : MLX5_TXOFF_PRE_DECL(i);
278 : : MLX5_TXOFF_PRE_DECL(v);
279 : : MLX5_TXOFF_PRE_DECL(iv);
280 : :
281 : : /* mlx5_tx_txpp.c */
282 : :
283 : : MLX5_TXOFF_PRE_DECL(full_ts_nompw);
284 : : MLX5_TXOFF_PRE_DECL(full_ts_nompwi);
285 : : MLX5_TXOFF_PRE_DECL(full_ts);
286 : : MLX5_TXOFF_PRE_DECL(full_ts_noi);
287 : : MLX5_TXOFF_PRE_DECL(none_ts);
288 : : MLX5_TXOFF_PRE_DECL(mdi_ts);
289 : : MLX5_TXOFF_PRE_DECL(mti_ts);
290 : : MLX5_TXOFF_PRE_DECL(mtiv_ts);
291 : :
292 : : /* mlx5_tx_mpw.c */
293 : :
294 : : MLX5_TXOFF_PRE_DECL(none_mpw);
295 : : MLX5_TXOFF_PRE_DECL(mci_mpw);
296 : : MLX5_TXOFF_PRE_DECL(mc_mpw);
297 : : MLX5_TXOFF_PRE_DECL(i_mpw);
298 : :
299 : : static __rte_always_inline struct mlx5_uar_data *
300 : : mlx5_tx_bfreg(struct mlx5_txq_data *txq)
301 : : {
302 : 0 : return &MLX5_PROC_PRIV(txq->port_id)->uar_table[txq->idx];
303 : : }
304 : :
305 : : /**
306 : : * Ring TX queue doorbell and flush the update by write memory barrier.
307 : : *
308 : : * @param txq
309 : : * Pointer to TX queue structure.
310 : : * @param wqe
311 : : * Pointer to the last WQE posted in the NIC.
312 : : */
313 : : static __rte_always_inline void
314 : : mlx5_tx_dbrec(struct mlx5_txq_data *txq, volatile struct mlx5_wqe *wqe)
315 : : {
316 : : mlx5_doorbell_ring(mlx5_tx_bfreg(txq), *(volatile uint64_t *)wqe,
317 : : txq->wqe_ci, txq->qp_db, 1);
318 : : }
319 : :
320 : : /**
321 : : * Convert timestamp from mbuf format to linear counter
322 : : * of Clock Queue completions (24 bits).
323 : : *
324 : : * @param sh
325 : : * Pointer to the device shared context to fetch Tx
326 : : * packet pacing timestamp and parameters.
327 : : * @param ts
328 : : * Timestamp from mbuf to convert.
329 : : * @return
330 : : * positive or zero value - completion ID to wait.
331 : : * negative value - conversion error.
332 : : */
333 : : static __rte_always_inline int32_t
334 : : mlx5_txpp_convert_tx_ts(struct mlx5_dev_ctx_shared *sh, uint64_t mts)
335 : : {
336 : : uint64_t ts, ci;
337 : : uint32_t tick;
338 : :
339 : : do {
340 : : /*
341 : : * Read atomically two uint64_t fields and compare lsb bits.
342 : : * It there is no match - the timestamp was updated in
343 : : * the service thread, data should be re-read.
344 : : */
345 : 0 : rte_compiler_barrier();
346 : 0 : ci = rte_atomic_load_explicit(&sh->txpp.ts.ci_ts, rte_memory_order_relaxed);
347 : 0 : ts = rte_atomic_load_explicit(&sh->txpp.ts.ts, rte_memory_order_relaxed);
348 : 0 : rte_compiler_barrier();
349 [ # # # # : 0 : if (!((ts ^ ci) << (64 - MLX5_CQ_INDEX_WIDTH)))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
350 : : break;
351 : : } while (true);
352 : : /* Perform the skew correction, positive value to send earlier. */
353 : 0 : mts -= sh->txpp.skew;
354 : 0 : mts -= ts;
355 [ # # # # : 0 : if (unlikely(mts >= UINT64_MAX / 2)) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
356 : : /* We have negative integer, mts is in the past. */
357 : 0 : rte_atomic_fetch_add_explicit(&sh->txpp.err_ts_past,
358 : : 1, rte_memory_order_relaxed);
359 : 0 : return -1;
360 : : }
361 : 0 : tick = sh->txpp.tick;
362 : : MLX5_ASSERT(tick);
363 : : /* Convert delta to completions, round up. */
364 : 0 : mts = (mts + tick - 1) / tick;
365 [ # # # # : 0 : if (unlikely(mts >= (1 << MLX5_CQ_INDEX_WIDTH) / 2 - 1)) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
366 : : /* We have mts is too distant future. */
367 : 0 : rte_atomic_fetch_add_explicit(&sh->txpp.err_ts_future,
368 : : 1, rte_memory_order_relaxed);
369 : 0 : return -1;
370 : : }
371 : 0 : mts <<= 64 - MLX5_CQ_INDEX_WIDTH;
372 : 0 : ci += mts;
373 : 0 : ci >>= 64 - MLX5_CQ_INDEX_WIDTH;
374 : 0 : return ci;
375 : : }
376 : :
377 : : /**
378 : : * Read real time clock counter directly from the device PCI BAR area.
379 : : * The PCI BAR must be mapped to the process memory space at initialization.
380 : : *
381 : : * @param dev
382 : : * Device to read clock counter from
383 : : *
384 : : * @return
385 : : * 0 - if HCA BAR is not supported or not mapped.
386 : : * !=0 - read 64-bit value of real-time in UTC formatv (nanoseconds)
387 : : */
388 : : static __rte_always_inline uint64_t mlx5_read_pcibar_clock(struct rte_eth_dev *dev)
389 : : {
390 : 0 : struct mlx5_proc_priv *ppriv = dev->process_private;
391 : :
392 [ # # # # ]: 0 : if (ppriv && ppriv->hca_bar) {
393 : : struct mlx5_priv *priv = dev->data->dev_private;
394 : : struct mlx5_dev_ctx_shared *sh = priv->sh;
395 : 0 : uint64_t *hca_ptr = (uint64_t *)(ppriv->hca_bar) +
396 : : __mlx5_64_off(initial_seg, real_time);
397 : : uint64_t __rte_atomic *ts_addr;
398 : : uint64_t ts;
399 : :
400 : : ts_addr = (uint64_t __rte_atomic *)hca_ptr;
401 : 0 : ts = rte_atomic_load_explicit(ts_addr, rte_memory_order_seq_cst);
402 [ # # ]: 0 : ts = rte_be_to_cpu_64(ts);
403 : : ts = mlx5_txpp_convert_rx_ts(sh, ts);
404 : : return ts;
405 : : }
406 : : return 0;
407 : : }
408 : :
409 : : static __rte_always_inline uint64_t mlx5_read_pcibar_clock_from_txq(struct mlx5_txq_data *txq)
410 : : {
411 : : struct mlx5_txq_ctrl *txq_ctrl = container_of(txq, struct mlx5_txq_ctrl, txq);
412 : : struct rte_eth_dev *dev = ETH_DEV(txq_ctrl->priv);
413 : :
414 : : return mlx5_read_pcibar_clock(dev);
415 : : }
416 : :
417 : : /**
418 : : * Set Software Parser flags and offsets in Ethernet Segment of WQE.
419 : : * Flags must be preliminary initialized to zero.
420 : : *
421 : : * @param loc
422 : : * Pointer to burst routine local context.
423 : : * @param swp_flags
424 : : * Pointer to store Software Parser flags.
425 : : * @param olx
426 : : * Configured Tx offloads mask. It is fully defined at
427 : : * compile time and may be used for optimization.
428 : : *
429 : : * @return
430 : : * Software Parser offsets packed in dword.
431 : : * Software Parser flags are set by pointer.
432 : : */
433 : : static __rte_always_inline uint32_t
434 : : txq_mbuf_to_swp(struct mlx5_txq_local *__rte_restrict loc,
435 : : uint8_t *swp_flags,
436 : : unsigned int olx)
437 : : {
438 : : uint64_t ol, tunnel;
439 : : unsigned int idx, off;
440 : : uint32_t set;
441 : :
442 : : if (!MLX5_TXOFF_CONFIG(SWP))
443 : : return 0;
444 : : ol = loc->mbuf->ol_flags;
445 : : tunnel = ol & RTE_MBUF_F_TX_TUNNEL_MASK;
446 : : /*
447 : : * Check whether Software Parser is required.
448 : : * Only customized tunnels may ask for.
449 : : */
450 : 0 : if (likely(tunnel != RTE_MBUF_F_TX_TUNNEL_UDP && tunnel != RTE_MBUF_F_TX_TUNNEL_IP))
451 : : return 0;
452 : : /*
453 : : * The index should have:
454 : : * bit[0:1] = RTE_MBUF_F_TX_L4_MASK
455 : : * bit[4] = RTE_MBUF_F_TX_IPV6
456 : : * bit[8] = RTE_MBUF_F_TX_OUTER_IPV6
457 : : * bit[9] = RTE_MBUF_F_TX_OUTER_UDP
458 : : */
459 : 0 : idx = (ol & (RTE_MBUF_F_TX_L4_MASK | RTE_MBUF_F_TX_IPV6 | RTE_MBUF_F_TX_OUTER_IPV6)) >> 52;
460 [ # # # # : 0 : idx |= (tunnel == RTE_MBUF_F_TX_TUNNEL_UDP) ? (1 << 9) : 0;
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
461 : 0 : *swp_flags = mlx5_swp_types_table[idx];
462 : : /*
463 : : * Set offsets for SW parser. Since ConnectX-5, SW parser just
464 : : * complements HW parser. SW parser starts to engage only if HW parser
465 : : * can't reach a header. For the older devices, HW parser will not kick
466 : : * in if any of SWP offsets is set. Therefore, all of the L3 offsets
467 : : * should be set regardless of HW offload.
468 : : */
469 : 0 : off = loc->mbuf->outer_l2_len;
470 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(VLAN) && ol & RTE_MBUF_F_TX_VLAN)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
471 : 0 : off += sizeof(struct rte_vlan_hdr);
472 : 0 : set = (off >> 1) << 8; /* Outer L3 offset. */
473 : 0 : off += loc->mbuf->outer_l3_len;
474 [ # # # # : 0 : if (tunnel == RTE_MBUF_F_TX_TUNNEL_UDP)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
475 : 0 : set |= off >> 1; /* Outer L4 offset. */
476 [ # # # # : 0 : if (ol & (RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IPV6)) { /* Inner IP. */
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
477 : 0 : const uint64_t csum = ol & RTE_MBUF_F_TX_L4_MASK;
478 : 0 : off += loc->mbuf->l2_len;
479 : 0 : set |= (off >> 1) << 24; /* Inner L3 offset. */
480 : 0 : if (csum == RTE_MBUF_F_TX_TCP_CKSUM ||
481 [ # # # # : 0 : csum == RTE_MBUF_F_TX_UDP_CKSUM ||
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
482 [ # # # # : 0 : (MLX5_TXOFF_CONFIG(TSO) && ol & RTE_MBUF_F_TX_TCP_SEG)) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # #
# ]
483 : 0 : off += loc->mbuf->l3_len;
484 : 0 : set |= (off >> 1) << 16; /* Inner L4 offset. */
485 : : }
486 : : }
487 : : set = rte_cpu_to_le_32(set);
488 : : return set;
489 : : }
490 : :
491 : : /**
492 : : * Convert the Checksum offloads to Verbs.
493 : : *
494 : : * @param buf
495 : : * Pointer to the mbuf.
496 : : *
497 : : * @return
498 : : * Converted checksum flags.
499 : : */
500 : : static __rte_always_inline uint8_t
501 : : txq_ol_cksum_to_cs(struct rte_mbuf *buf)
502 : : {
503 : : uint32_t idx;
504 : 0 : uint8_t is_tunnel = !!(buf->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK);
505 : : const uint64_t ol_flags_mask = RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_L4_MASK |
506 : : RTE_MBUF_F_TX_IP_CKSUM | RTE_MBUF_F_TX_OUTER_IP_CKSUM;
507 : :
508 : : /*
509 : : * The index should have:
510 : : * bit[0] = RTE_MBUF_F_TX_TCP_SEG
511 : : * bit[2:3] = RTE_MBUF_F_TX_UDP_CKSUM, RTE_MBUF_F_TX_TCP_CKSUM
512 : : * bit[4] = RTE_MBUF_F_TX_IP_CKSUM
513 : : * bit[8] = RTE_MBUF_F_TX_OUTER_IP_CKSUM
514 : : * bit[9] = tunnel
515 : : */
516 [ # # # # : 0 : idx = ((buf->ol_flags & ol_flags_mask) >> 50) | (!!is_tunnel << 9);
# # # # #
# # # ]
517 : 0 : return mlx5_cksum_table[idx];
518 : : }
519 : :
520 : : /**
521 : : * Free the mbufs from the linear array of pointers.
522 : : *
523 : : * @param txq
524 : : * Pointer to Tx queue structure.
525 : : * @param pkts
526 : : * Pointer to array of packets to be free.
527 : : * @param pkts_n
528 : : * Number of packets to be freed.
529 : : * @param olx
530 : : * Configured Tx offloads mask. It is fully defined at
531 : : * compile time and may be used for optimization.
532 : : */
533 : : static __rte_always_inline void
534 : : mlx5_tx_free_mbuf(struct mlx5_txq_data *__rte_restrict txq,
535 : : struct rte_mbuf **__rte_restrict pkts,
536 : : unsigned int pkts_n,
537 : : unsigned int olx __rte_unused)
538 : : {
539 : : struct rte_mempool *pool = NULL;
540 : : struct rte_mbuf **p_free = NULL;
541 : : struct rte_mbuf *mbuf;
542 : : unsigned int n_free = 0;
543 : :
544 : : /*
545 : : * The implemented algorithm eliminates
546 : : * copying pointers to temporary array
547 : : * for rte_mempool_put_bulk() calls.
548 : : */
549 : : MLX5_ASSERT(pkts);
550 : : MLX5_ASSERT(pkts_n);
551 : : /*
552 : : * Free mbufs directly to the pool in bulk
553 : : * if fast free offload is engaged
554 : : */
555 [ # # ]: 0 : if (!MLX5_TXOFF_CONFIG(MULTI) && txq->fast_free) {
556 : 0 : mbuf = *pkts;
557 [ # # ]: 0 : pool = mbuf->pool;
558 : : rte_mempool_put_bulk(pool, (void *)pkts, pkts_n);
559 : : return;
560 : : }
561 : : for (;;) {
562 : : for (;;) {
563 : : /*
564 : : * Decrement mbuf reference counter, detach
565 : : * indirect and external buffers if needed.
566 : : */
567 [ # # ]: 0 : mbuf = rte_pktmbuf_prefree_seg(*pkts);
568 [ # # ]: 0 : if (likely(mbuf != NULL)) {
569 : : MLX5_ASSERT(mbuf == *pkts);
570 [ # # ]: 0 : if (likely(n_free != 0)) {
571 [ # # ]: 0 : if (unlikely(pool != mbuf->pool))
572 : : /* From different pool. */
573 : : break;
574 : : } else {
575 : : /* Start new scan array. */
576 : 0 : pool = mbuf->pool;
577 : : p_free = pkts;
578 : : }
579 : 0 : ++n_free;
580 : 0 : ++pkts;
581 : 0 : --pkts_n;
582 [ # # ]: 0 : if (unlikely(pkts_n == 0)) {
583 : : mbuf = NULL;
584 : : break;
585 : : }
586 : : } else {
587 : : /*
588 : : * This happens if mbuf is still referenced.
589 : : * We can't put it back to the pool, skip.
590 : : */
591 : 0 : ++pkts;
592 : 0 : --pkts_n;
593 [ # # ]: 0 : if (unlikely(n_free != 0))
594 : : /* There is some array to free.*/
595 : : break;
596 [ # # ]: 0 : if (unlikely(pkts_n == 0))
597 : : /* Last mbuf, nothing to free. */
598 : : return;
599 : : }
600 : : }
601 : : for (;;) {
602 : : /*
603 : : * This loop is implemented to avoid multiple
604 : : * inlining of rte_mempool_put_bulk().
605 : : */
606 : 0 : MLX5_ASSERT(pool);
607 : : MLX5_ASSERT(p_free);
608 : : MLX5_ASSERT(n_free);
609 : : /*
610 : : * Free the array of pre-freed mbufs
611 : : * belonging to the same memory pool.
612 : : */
613 : : rte_mempool_put_bulk(pool, (void *)p_free, n_free);
614 [ # # ]: 0 : if (unlikely(mbuf != NULL)) {
615 : : /* There is the request to start new scan. */
616 : 0 : pool = mbuf->pool;
617 : 0 : p_free = pkts++;
618 : : n_free = 1;
619 : 0 : --pkts_n;
620 [ # # ]: 0 : if (likely(pkts_n != 0))
621 : : break;
622 : : /*
623 : : * This is the last mbuf to be freed.
624 : : * Do one more loop iteration to complete.
625 : : * This is rare case of the last unique mbuf.
626 : : */
627 : : mbuf = NULL;
628 : : continue;
629 : : }
630 [ # # ]: 0 : if (likely(pkts_n == 0))
631 : : return;
632 : : n_free = 0;
633 : : break;
634 : : }
635 : : }
636 : : }
637 : :
638 : : /**
639 : : * No inline version to free buffers for optimal call
640 : : * on the tx_burst completion.
641 : : */
642 : : static __rte_noinline void
643 [ # # ]: 0 : __mlx5_tx_free_mbuf(struct mlx5_txq_data *__rte_restrict txq,
644 : : struct rte_mbuf **__rte_restrict pkts,
645 : : unsigned int pkts_n,
646 : : unsigned int olx __rte_unused)
647 : : {
648 : : mlx5_tx_free_mbuf(txq, pkts, pkts_n, olx);
649 : 0 : }
650 : :
651 : : /**
652 : : * Free the mbuf from the elts ring buffer till new tail.
653 : : *
654 : : * @param txq
655 : : * Pointer to Tx queue structure.
656 : : * @param tail
657 : : * Index in elts to free up to, becomes new elts tail.
658 : : * @param olx
659 : : * Configured Tx offloads mask. It is fully defined at
660 : : * compile time and may be used for optimization.
661 : : */
662 : : static __rte_always_inline void
663 : : mlx5_tx_free_elts(struct mlx5_txq_data *__rte_restrict txq,
664 : : uint16_t tail,
665 : : unsigned int olx __rte_unused)
666 : : {
667 : 0 : uint16_t n_elts = tail - txq->elts_tail;
668 : :
669 : : MLX5_ASSERT(n_elts);
670 : : MLX5_ASSERT(n_elts <= txq->elts_s);
671 : : /*
672 : : * Implement a loop to support ring buffer wraparound
673 : : * with single inlining of mlx5_tx_free_mbuf().
674 : : */
675 : : do {
676 : : unsigned int part;
677 : :
678 : 0 : part = txq->elts_s - (txq->elts_tail & txq->elts_m);
679 : 0 : part = RTE_MIN(part, n_elts);
680 : : MLX5_ASSERT(part);
681 : : MLX5_ASSERT(part <= txq->elts_s);
682 [ # # ]: 0 : mlx5_tx_free_mbuf(txq,
683 : : &txq->elts[txq->elts_tail & txq->elts_m],
684 : : part, olx);
685 : 0 : txq->elts_tail += part;
686 : 0 : n_elts -= part;
687 [ # # ]: 0 : } while (n_elts);
688 : : }
689 : :
690 : : /**
691 : : * Store the mbuf being sent into elts ring buffer.
692 : : * On Tx completion these mbufs will be freed.
693 : : *
694 : : * @param txq
695 : : * Pointer to Tx queue structure.
696 : : * @param pkts
697 : : * Pointer to array of packets to be stored.
698 : : * @param pkts_n
699 : : * Number of packets to be stored.
700 : : * @param olx
701 : : * Configured Tx offloads mask. It is fully defined at
702 : : * compile time and may be used for optimization.
703 : : */
704 : : static __rte_always_inline void
705 : : mlx5_tx_copy_elts(struct mlx5_txq_data *__rte_restrict txq,
706 : : struct rte_mbuf **__rte_restrict pkts,
707 : : unsigned int pkts_n,
708 : : unsigned int olx __rte_unused)
709 : : {
710 : : unsigned int part;
711 : 0 : struct rte_mbuf **elts = (struct rte_mbuf **)txq->elts;
712 : :
713 : : MLX5_ASSERT(pkts);
714 : : MLX5_ASSERT(pkts_n);
715 : 0 : part = txq->elts_s - (txq->elts_head & txq->elts_m);
716 : : MLX5_ASSERT(part);
717 : : MLX5_ASSERT(part <= txq->elts_s);
718 : : /* This code is a good candidate for vectorizing with SIMD. */
719 : 0 : rte_memcpy((void *)(elts + (txq->elts_head & txq->elts_m)),
720 : : (void *)pkts,
721 : 0 : RTE_MIN(part, pkts_n) * sizeof(struct rte_mbuf *));
722 : 0 : txq->elts_head += pkts_n;
723 [ # # # # : 0 : if (unlikely(part < pkts_n))
# # # # #
# # # # #
# # # # #
# # # ]
724 : : /* The copy is wrapping around the elts array. */
725 : 0 : rte_memcpy((void *)elts, (void *)(pkts + part),
726 [ # # # # : 0 : (pkts_n - part) * sizeof(struct rte_mbuf *));
# # # # #
# # # # #
# # # # #
# # # ]
727 : : }
728 : :
729 : : /**
730 : : * Check if the completion request flag should be set in the last WQE.
731 : : * Both pushed mbufs and WQEs are monitored and the completion request
732 : : * flag is set if any of thresholds is reached.
733 : : *
734 : : * @param txq
735 : : * Pointer to TX queue structure.
736 : : * @param loc
737 : : * Pointer to burst routine local context.
738 : : * @param olx
739 : : * Configured Tx offloads mask. It is fully defined at
740 : : * compile time and may be used for optimization.
741 : : */
742 : : static __rte_always_inline void
743 : : mlx5_tx_request_completion(struct mlx5_txq_data *__rte_restrict txq,
744 : : struct mlx5_txq_local *__rte_restrict loc,
745 : : unsigned int olx)
746 : : {
747 : 0 : uint16_t head = txq->elts_head;
748 : : unsigned int part;
749 : :
750 : : part = MLX5_TXOFF_CONFIG(INLINE) ?
751 : 0 : 0 : loc->pkts_sent - loc->pkts_copy;
752 : 0 : head += part;
753 [ # # # # : 0 : if ((uint16_t)(head - txq->elts_comp) >= MLX5_TX_COMP_THRESH ||
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
754 : 0 : (MLX5_TXOFF_CONFIG(INLINE) &&
755 [ # # # # : 0 : (uint16_t)(txq->wqe_ci - txq->wqe_comp) >= txq->wqe_thres)) {
# # # # #
# # # #
# ]
756 : : volatile struct mlx5_wqe *last = loc->wqe_last;
757 : :
758 : : MLX5_ASSERT(last);
759 : 0 : txq->elts_comp = head;
760 : : if (MLX5_TXOFF_CONFIG(INLINE))
761 : 0 : txq->wqe_comp = txq->wqe_ci;
762 : : /* Request unconditional completion on last WQE. */
763 : 0 : last->cseg.flags = RTE_BE32(MLX5_COMP_ALWAYS <<
764 : : MLX5_COMP_MODE_OFFSET);
765 : : /* Save elts_head in dedicated free on completion queue. */
766 : : #ifdef RTE_LIBRTE_MLX5_DEBUG
767 : : txq->fcqs[txq->cq_pi++ & txq->cqe_m] = head |
768 : : (last->cseg.opcode >> 8) << 16;
769 : : #else
770 : 0 : txq->fcqs[txq->cq_pi++ & txq->cqe_m] = head;
771 : : #endif
772 : : /* A CQE slot must always be available. */
773 : : MLX5_ASSERT((txq->cq_pi - txq->cq_ci) <= txq->cqe_s);
774 : : }
775 : : }
776 : :
777 : : /**
778 : : * Set completion request flag for all issued WQEs.
779 : : * This routine is intended to be used with enabled fast path tracing
780 : : * and send scheduling on time to provide the detailed report in trace
781 : : * for send completions on every WQE.
782 : : *
783 : : * @param txq
784 : : * Pointer to TX queue structure.
785 : : * @param loc
786 : : * Pointer to burst routine local context.
787 : : * @param olx
788 : : * Configured Tx offloads mask. It is fully defined at
789 : : * compile time and may be used for optimization.
790 : : */
791 : : static __rte_always_inline void
792 : : mlx5_tx_request_completion_trace(struct mlx5_txq_data *__rte_restrict txq,
793 : : struct mlx5_txq_local *__rte_restrict loc,
794 : : unsigned int olx)
795 : : {
796 : : uint16_t head = txq->elts_comp;
797 : :
798 : : while (txq->wqe_comp != txq->wqe_ci) {
799 : : volatile struct mlx5_wqe *wqe;
800 : : uint32_t wqe_n;
801 : :
802 : : MLX5_ASSERT(loc->wqe_last);
803 : : wqe = txq->wqes + (txq->wqe_comp & txq->wqe_m);
804 : : if (wqe == loc->wqe_last) {
805 : : head = txq->elts_head;
806 : : head += MLX5_TXOFF_CONFIG(INLINE) ?
807 : : 0 : loc->pkts_sent - loc->pkts_copy;
808 : : txq->elts_comp = head;
809 : : }
810 : : /* Completion request flag was set on cseg constructing. */
811 : : #ifdef RTE_LIBRTE_MLX5_DEBUG
812 : : txq->fcqs[txq->cq_pi++ & txq->cqe_m] = head |
813 : : (wqe->cseg.opcode >> 8) << 16;
814 : : #else
815 : : txq->fcqs[txq->cq_pi++ & txq->cqe_m] = head;
816 : : #endif
817 : : /* A CQE slot must always be available. */
818 : : MLX5_ASSERT((txq->cq_pi - txq->cq_ci) <= txq->cqe_s);
819 : : /* Advance to the next WQE in the queue. */
820 : : wqe_n = rte_be_to_cpu_32(wqe->cseg.sq_ds) & 0x3F;
821 : : txq->wqe_comp += RTE_ALIGN(wqe_n, 4) / 4;
822 : : }
823 : : }
824 : :
825 : : /**
826 : : * Build the Control Segment with specified opcode:
827 : : * - MLX5_OPCODE_SEND
828 : : * - MLX5_OPCODE_ENHANCED_MPSW
829 : : * - MLX5_OPCODE_TSO
830 : : *
831 : : * @param txq
832 : : * Pointer to TX queue structure.
833 : : * @param loc
834 : : * Pointer to burst routine local context.
835 : : * @param wqe
836 : : * Pointer to WQE to fill with built Control Segment.
837 : : * @param ds
838 : : * Supposed length of WQE in segments.
839 : : * @param opcode
840 : : * SQ WQE opcode to put into Control Segment.
841 : : * @param olx
842 : : * Configured Tx offloads mask. It is fully defined at
843 : : * compile time and may be used for optimization.
844 : : */
845 : : static __rte_always_inline void
846 : : mlx5_tx_cseg_init(struct mlx5_txq_data *__rte_restrict txq,
847 : : struct mlx5_txq_local *__rte_restrict loc __rte_unused,
848 : : struct mlx5_wqe *__rte_restrict wqe,
849 : : unsigned int ds,
850 : : unsigned int opcode,
851 : : unsigned int olx)
852 : : {
853 : : struct mlx5_wqe_cseg *__rte_restrict cs = &wqe->cseg;
854 : : uint64_t real_time;
855 : :
856 : : /* For legacy MPW replace the EMPW by TSO with modifier. */
857 : : if (MLX5_TXOFF_CONFIG(MPW) && opcode == MLX5_OPCODE_ENHANCED_MPSW)
858 : : opcode = MLX5_OPCODE_TSO | MLX5_OPC_MOD_MPW << 24;
859 [ # # # # : 0 : cs->opcode = rte_cpu_to_be_32((txq->wqe_ci << 8) | opcode);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
860 [ # # # # : 0 : cs->sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | ds);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
861 : : if (MLX5_TXOFF_CONFIG(TXPP) && __rte_trace_point_fp_is_enabled())
862 : : cs->flags = RTE_BE32(MLX5_COMP_ALWAYS <<
863 : : MLX5_COMP_MODE_OFFSET);
864 : : else
865 : 0 : cs->flags = RTE_BE32(MLX5_COMP_ONLY_FIRST_ERR <<
866 : : MLX5_COMP_MODE_OFFSET);
867 [ # # # # : 0 : cs->misc = RTE_BE32(0);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
868 : : if (__rte_trace_point_fp_is_enabled()) {
869 : : real_time = mlx5_read_pcibar_clock_from_txq(txq);
870 : : if (!loc->pkts_sent)
871 : : rte_pmd_mlx5_trace_tx_entry(real_time, txq->port_id, txq->idx);
872 : : rte_pmd_mlx5_trace_tx_wqe(real_time, (txq->wqe_ci << 8) | opcode);
873 : : }
874 : : }
875 : :
876 : : /**
877 : : * Build the Synchronize Queue Segment with specified completion index.
878 : : *
879 : : * @param txq
880 : : * Pointer to TX queue structure.
881 : : * @param loc
882 : : * Pointer to burst routine local context.
883 : : * @param wqe
884 : : * Pointer to WQE to fill with built Control Segment.
885 : : * @param wci
886 : : * Completion index in Clock Queue to wait.
887 : : * @param olx
888 : : * Configured Tx offloads mask. It is fully defined at
889 : : * compile time and may be used for optimization.
890 : : */
891 : : static __rte_always_inline void
892 : : mlx5_tx_qseg_init(struct mlx5_txq_data *restrict txq,
893 : : struct mlx5_txq_local *restrict loc __rte_unused,
894 : : struct mlx5_wqe *restrict wqe,
895 : : unsigned int wci,
896 : : unsigned int olx __rte_unused)
897 : : {
898 : : struct mlx5_wqe_qseg *qs;
899 : :
900 : 0 : qs = RTE_PTR_ADD(wqe, MLX5_WSEG_SIZE);
901 : 0 : qs->max_index = rte_cpu_to_be_32(wci);
902 [ # # # # : 0 : qs->qpn_cqn = rte_cpu_to_be_32(txq->sh->txpp.clock_queue.cq_obj.cq->id);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
903 : 0 : qs->reserved0 = RTE_BE32(0);
904 : 0 : qs->reserved1 = RTE_BE32(0);
905 : 0 : }
906 : :
907 : : /**
908 : : * Build the Wait on Time Segment with specified timestamp value.
909 : : *
910 : : * @param txq
911 : : * Pointer to TX queue structure.
912 : : * @param loc
913 : : * Pointer to burst routine local context.
914 : : * @param wqe
915 : : * Pointer to WQE to fill with built Control Segment.
916 : : * @param ts
917 : : * Timesatmp value to wait.
918 : : * @param olx
919 : : * Configured Tx offloads mask. It is fully defined at
920 : : * compile time and may be used for optimization.
921 : : */
922 : : static __rte_always_inline void
923 : : mlx5_tx_wseg_init(struct mlx5_txq_data *restrict txq,
924 : : struct mlx5_txq_local *restrict loc __rte_unused,
925 : : struct mlx5_wqe *restrict wqe,
926 : : uint64_t ts,
927 : : unsigned int olx __rte_unused)
928 : : {
929 : : struct mlx5_wqe_wseg *ws;
930 : :
931 : 0 : ws = RTE_PTR_ADD(wqe, MLX5_WSEG_SIZE);
932 : 0 : ws->operation = rte_cpu_to_be_32(MLX5_WAIT_COND_CYCLIC_SMALLER);
933 : 0 : ws->lkey = RTE_BE32(0);
934 : 0 : ws->va_high = RTE_BE32(0);
935 : 0 : ws->va_low = RTE_BE32(0);
936 [ # # # # : 0 : if (txq->rt_timestamp) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
937 : 0 : ts = ts % (uint64_t)NS_PER_S
938 : 0 : | (ts / (uint64_t)NS_PER_S) << 32;
939 : : }
940 [ # # # # : 0 : ws->value = rte_cpu_to_be_64(ts);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
941 : 0 : ws->mask = txq->rt_timemask;
942 : 0 : }
943 : :
944 : : /**
945 : : * Build the Ethernet Segment without inlined data.
946 : : * Supports Software Parser, Checksums and VLAN insertion Tx offload features.
947 : : *
948 : : * @param txq
949 : : * Pointer to TX queue structure.
950 : : * @param loc
951 : : * Pointer to burst routine local context.
952 : : * @param wqe
953 : : * Pointer to WQE to fill with built Ethernet Segment.
954 : : * @param olx
955 : : * Configured Tx offloads mask. It is fully defined at
956 : : * compile time and may be used for optimization.
957 : : */
958 : : static __rte_always_inline void
959 : : mlx5_tx_eseg_none(struct mlx5_txq_data *__rte_restrict txq __rte_unused,
960 : : struct mlx5_txq_local *__rte_restrict loc,
961 : : struct mlx5_wqe *__rte_restrict wqe,
962 : : unsigned int olx)
963 : : {
964 : : struct mlx5_wqe_eseg *__rte_restrict es = &wqe->eseg;
965 : : uint32_t csum;
966 : :
967 : : /*
968 : : * Calculate and set check sum flags first, dword field
969 : : * in segment may be shared with Software Parser flags.
970 : : */
971 : 0 : csum = MLX5_TXOFF_CONFIG(CSUM) ? txq_ol_cksum_to_cs(loc->mbuf) : 0;
972 [ # # # # : 0 : es->flags = rte_cpu_to_le_32(csum);
# # # # #
# # # # #
# # # # #
# # # ]
973 : : /*
974 : : * Calculate and set Software Parser offsets and flags.
975 : : * These flags a set for custom UDP and IP tunnel packets.
976 : : */
977 : 0 : es->swp_offs = txq_mbuf_to_swp(loc, &es->swp_flags, olx);
978 : : /* Fill metadata field if needed. */
979 : 0 : es->metadata = MLX5_TXOFF_CONFIG(METADATA) ?
980 : 0 : loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
981 [ # # # # : 0 : rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) :
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
982 : : 0 : 0;
983 : : /* Engage VLAN tag insertion feature if requested. */
984 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(VLAN) &&
# # # # ]
985 [ # # # # : 0 : loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) {
# # # # #
# # # # #
# # ]
986 : : /*
987 : : * We should get here only if device support
988 : : * this feature correctly.
989 : : */
990 : : MLX5_ASSERT(txq->vlan_en);
991 [ # # # # : 0 : es->inline_hdr = rte_cpu_to_be_32(MLX5_ETH_WQE_VLAN_INSERT |
# # # # #
# # # # #
# # # # #
# ]
992 : : loc->mbuf->vlan_tci);
993 : : } else {
994 : 0 : es->inline_hdr = RTE_BE32(0);
995 : : }
996 : : }
997 : :
998 : : /**
999 : : * Build the Ethernet Segment with minimal inlined data
1000 : : * of MLX5_ESEG_MIN_INLINE_SIZE bytes length. This is
1001 : : * used to fill the gap in single WQEBB WQEs.
1002 : : * Supports Software Parser, Checksums and VLAN
1003 : : * insertion Tx offload features.
1004 : : *
1005 : : * @param txq
1006 : : * Pointer to TX queue structure.
1007 : : * @param loc
1008 : : * Pointer to burst routine local context.
1009 : : * @param wqe
1010 : : * Pointer to WQE to fill with built Ethernet Segment.
1011 : : * @param vlan
1012 : : * Length of VLAN tag insertion if any.
1013 : : * @param olx
1014 : : * Configured Tx offloads mask. It is fully defined at
1015 : : * compile time and may be used for optimization.
1016 : : */
1017 : : static __rte_always_inline void
1018 : : mlx5_tx_eseg_dmin(struct mlx5_txq_data *__rte_restrict txq __rte_unused,
1019 : : struct mlx5_txq_local *__rte_restrict loc,
1020 : : struct mlx5_wqe *__rte_restrict wqe,
1021 : : unsigned int vlan,
1022 : : unsigned int olx)
1023 : : {
1024 : : struct mlx5_wqe_eseg *__rte_restrict es = &wqe->eseg;
1025 : : uint32_t csum;
1026 : : uint8_t *psrc, *pdst;
1027 : :
1028 : : /*
1029 : : * Calculate and set check sum flags first, dword field
1030 : : * in segment may be shared with Software Parser flags.
1031 : : */
1032 : 0 : csum = MLX5_TXOFF_CONFIG(CSUM) ? txq_ol_cksum_to_cs(loc->mbuf) : 0;
1033 [ # # # # : 0 : es->flags = rte_cpu_to_le_32(csum);
# # ]
1034 : : /*
1035 : : * Calculate and set Software Parser offsets and flags.
1036 : : * These flags a set for custom UDP and IP tunnel packets.
1037 : : */
1038 : 0 : es->swp_offs = txq_mbuf_to_swp(loc, &es->swp_flags, olx);
1039 : : /* Fill metadata field if needed. */
1040 : 0 : es->metadata = MLX5_TXOFF_CONFIG(METADATA) ?
1041 : 0 : loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
1042 [ # # # # : 0 : rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) :
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
1043 : : 0 : 0;
1044 : 0 : psrc = rte_pktmbuf_mtod(loc->mbuf, uint8_t *);
1045 : 0 : es->inline_hdr_sz = RTE_BE16(MLX5_ESEG_MIN_INLINE_SIZE);
1046 [ # # # # : 0 : es->inline_data = *(unaligned_uint16_t *)psrc;
# # ]
1047 : 0 : psrc += sizeof(uint16_t);
1048 : 0 : pdst = (uint8_t *)(es + 1);
1049 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(VLAN) && vlan) {
# # # # ]
1050 : : /* Implement VLAN tag insertion as part inline data. */
1051 : : memcpy(pdst, psrc, 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t));
1052 : : pdst += 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t);
1053 : : psrc += 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t);
1054 : : /* Insert VLAN ethertype + VLAN tag. */
1055 [ # # # # : 0 : *(unaligned_uint32_t *)pdst = rte_cpu_to_be_32
# # # # ]
1056 : : ((RTE_ETHER_TYPE_VLAN << 16) |
1057 : : loc->mbuf->vlan_tci);
1058 : : pdst += sizeof(struct rte_vlan_hdr);
1059 : : /* Copy the rest two bytes from packet data. */
1060 : : MLX5_ASSERT(pdst == RTE_PTR_ALIGN(pdst, sizeof(uint16_t)));
1061 : 0 : *(uint16_t *)pdst = *(unaligned_uint16_t *)psrc;
1062 : : } else {
1063 : : /* Fill the gap in the title WQEBB with inline data. */
1064 : : rte_mov16(pdst, psrc);
1065 : : }
1066 : : }
1067 : :
1068 : : /**
1069 : : * Build the Ethernet Segment with entire packet data inlining. Checks the
1070 : : * boundary of WQEBB and ring buffer wrapping, supports Software Parser,
1071 : : * Checksums and VLAN insertion Tx offload features.
1072 : : *
1073 : : * @param txq
1074 : : * Pointer to TX queue structure.
1075 : : * @param loc
1076 : : * Pointer to burst routine local context.
1077 : : * @param wqe
1078 : : * Pointer to WQE to fill with built Ethernet Segment.
1079 : : * @param vlan
1080 : : * Length of VLAN tag insertion if any.
1081 : : * @param inlen
1082 : : * Length of data to inline (VLAN included, if any).
1083 : : * @param tso
1084 : : * TSO flag, set mss field from the packet.
1085 : : * @param olx
1086 : : * Configured Tx offloads mask. It is fully defined at
1087 : : * compile time and may be used for optimization.
1088 : : *
1089 : : * @return
1090 : : * Pointer to the next Data Segment (aligned and wrapped around).
1091 : : */
1092 : : static __rte_always_inline struct mlx5_wqe_dseg *
1093 : : mlx5_tx_eseg_data(struct mlx5_txq_data *__rte_restrict txq,
1094 : : struct mlx5_txq_local *__rte_restrict loc,
1095 : : struct mlx5_wqe *__rte_restrict wqe,
1096 : : unsigned int vlan,
1097 : : unsigned int inlen,
1098 : : unsigned int tso,
1099 : : unsigned int olx)
1100 : : {
1101 : : struct mlx5_wqe_eseg *__rte_restrict es = &wqe->eseg;
1102 : : uint32_t csum;
1103 : : uint8_t *psrc, *pdst;
1104 : : unsigned int part;
1105 : :
1106 : : /*
1107 : : * Calculate and set check sum flags first, dword field
1108 : : * in segment may be shared with Software Parser flags.
1109 : : */
1110 : 0 : csum = MLX5_TXOFF_CONFIG(CSUM) ? txq_ol_cksum_to_cs(loc->mbuf) : 0;
1111 : : if (tso) {
1112 : 0 : csum <<= 24;
1113 : 0 : csum |= loc->mbuf->tso_segsz;
1114 [ # # # # : 0 : es->flags = rte_cpu_to_be_32(csum);
# # # # #
# # # # #
# # # # #
# ]
1115 : : } else {
1116 [ # # # # : 0 : es->flags = rte_cpu_to_le_32(csum);
# # # # #
# # # ]
1117 : : }
1118 : : /*
1119 : : * Calculate and set Software Parser offsets and flags.
1120 : : * These flags a set for custom UDP and IP tunnel packets.
1121 : : */
1122 : 0 : es->swp_offs = txq_mbuf_to_swp(loc, &es->swp_flags, olx);
1123 : : /* Fill metadata field if needed. */
1124 : 0 : es->metadata = MLX5_TXOFF_CONFIG(METADATA) ?
1125 : 0 : loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
1126 [ # # # # : 0 : rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) :
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
1127 : : 0 : 0;
1128 : 0 : psrc = rte_pktmbuf_mtod(loc->mbuf, uint8_t *);
1129 [ # # # # : 0 : es->inline_hdr_sz = rte_cpu_to_be_16(inlen);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1130 [ # # # # : 0 : es->inline_data = *(unaligned_uint16_t *)psrc;
# # # # #
# # # ]
1131 : 0 : psrc += sizeof(uint16_t);
1132 : 0 : pdst = (uint8_t *)(es + 1);
1133 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(VLAN) && vlan) {
# # # # #
# # # # #
# # # # #
# # # ]
1134 : : /* Implement VLAN tag insertion as part inline data. */
1135 : : memcpy(pdst, psrc, 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t));
1136 : : pdst += 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t);
1137 : : psrc += 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t);
1138 : : /* Insert VLAN ethertype + VLAN tag. */
1139 [ # # # # : 0 : *(unaligned_uint32_t *)pdst = rte_cpu_to_be_32
# # # # #
# # # # #
# # # # #
# # # ]
1140 : : ((RTE_ETHER_TYPE_VLAN << 16) |
1141 : : loc->mbuf->vlan_tci);
1142 : : pdst += sizeof(struct rte_vlan_hdr);
1143 : : /* Copy the rest two bytes from packet data. */
1144 : : MLX5_ASSERT(pdst == RTE_PTR_ALIGN(pdst, sizeof(uint16_t)));
1145 : 0 : *(uint16_t *)pdst = *(unaligned_uint16_t *)psrc;
1146 : 0 : psrc += sizeof(uint16_t);
1147 : : } else {
1148 : : /* Fill the gap in the title WQEBB with inline data. */
1149 : : rte_mov16(pdst, psrc);
1150 : 0 : psrc += MLX5_SIZE_MOV16;
1151 : : }
1152 : 0 : pdst = (uint8_t *)(es + 2);
1153 : : MLX5_ASSERT(inlen >= MLX5_ESEG_MIN_INLINE_SIZE);
1154 : : MLX5_ASSERT(pdst < (uint8_t *)txq->wqes_end);
1155 : 0 : inlen -= MLX5_ESEG_MIN_INLINE_SIZE;
1156 [ # # # # : 0 : if (!inlen) {
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1157 : : MLX5_ASSERT(pdst == RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE));
1158 : : return (struct mlx5_wqe_dseg *)pdst;
1159 : : }
1160 : : /*
1161 : : * The WQEBB space availability is checked by caller.
1162 : : * Here we should be aware of WQE ring buffer wraparound only.
1163 : : */
1164 : 0 : part = (uint8_t *)txq->wqes_end - pdst;
1165 : 0 : part = RTE_MIN(part, inlen);
1166 : : do {
1167 [ # # # # : 0 : rte_memcpy(pdst, psrc, part);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1168 : 0 : inlen -= part;
1169 [ # # # # : 0 : if (likely(!inlen)) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1170 : : /*
1171 : : * If return value is not used by the caller
1172 : : * the code below will be optimized out.
1173 : : */
1174 : 0 : pdst += part;
1175 : 0 : pdst = RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE);
1176 [ # # # # : 0 : if (unlikely(pdst >= (uint8_t *)txq->wqes_end))
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1177 : 0 : pdst = (uint8_t *)txq->wqes;
1178 : : return (struct mlx5_wqe_dseg *)pdst;
1179 : : }
1180 : 0 : pdst = (uint8_t *)txq->wqes;
1181 : 0 : psrc += part;
1182 : : part = inlen;
1183 : : } while (true);
1184 : : }
1185 : :
1186 : : /**
1187 : : * Copy data from chain of mbuf to the specified linear buffer.
1188 : : * Checksums and VLAN insertion Tx offload features. If data
1189 : : * from some mbuf copied completely this mbuf is freed. Local
1190 : : * structure is used to keep the byte stream state.
1191 : : *
1192 : : * @param pdst
1193 : : * Pointer to the destination linear buffer.
1194 : : * @param loc
1195 : : * Pointer to burst routine local context.
1196 : : * @param len
1197 : : * Length of data to be copied.
1198 : : * @param must
1199 : : * Length of data to be copied ignoring no inline hint.
1200 : : * @param olx
1201 : : * Configured Tx offloads mask. It is fully defined at
1202 : : * compile time and may be used for optimization.
1203 : : *
1204 : : * @return
1205 : : * Number of actual copied data bytes. This is always greater than or
1206 : : * equal to must parameter and might be lesser than len in no inline
1207 : : * hint flag is encountered.
1208 : : */
1209 : : static __rte_always_inline unsigned int
1210 : : mlx5_tx_mseg_memcpy(uint8_t *pdst,
1211 : : struct mlx5_txq_local *__rte_restrict loc,
1212 : : unsigned int len,
1213 : : unsigned int must,
1214 : : unsigned int olx __rte_unused)
1215 : : {
1216 : : struct rte_mbuf *mbuf;
1217 : : unsigned int part, dlen, copy = 0;
1218 : : uint8_t *psrc;
1219 : :
1220 : : MLX5_ASSERT(len);
1221 : : do {
1222 : : /* Allow zero length packets, must check first. */
1223 : 0 : dlen = rte_pktmbuf_data_len(loc->mbuf);
1224 [ # # # # : 0 : if (dlen <= loc->mbuf_off) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1225 : : /* Exhausted packet, just free. */
1226 : : mbuf = loc->mbuf;
1227 : 0 : loc->mbuf = mbuf->next;
1228 : : rte_pktmbuf_free_seg(mbuf);
1229 : : loc->mbuf_off = 0;
1230 : : MLX5_ASSERT(loc->mbuf_nseg > 1);
1231 : : MLX5_ASSERT(loc->mbuf);
1232 : 0 : --loc->mbuf_nseg;
1233 [ # # # # : 0 : if (loc->mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1234 : : unsigned int diff;
1235 : :
1236 [ # # # # : 0 : if (copy >= must) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1237 : : /*
1238 : : * We already copied the minimal
1239 : : * requested amount of data.
1240 : : */
1241 : : return copy;
1242 : : }
1243 : 0 : diff = must - copy;
1244 [ # # # # : 0 : if (diff <= rte_pktmbuf_data_len(loc->mbuf)) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1245 : : /*
1246 : : * Copy only the minimal required
1247 : : * part of the data buffer. Limit amount
1248 : : * of data to be copied to the length of
1249 : : * available space.
1250 : : */
1251 : 0 : len = RTE_MIN(len, diff);
1252 : : }
1253 : : }
1254 : 0 : continue;
1255 : : }
1256 : 0 : dlen -= loc->mbuf_off;
1257 : 0 : psrc = rte_pktmbuf_mtod_offset(loc->mbuf, uint8_t *,
1258 : : loc->mbuf_off);
1259 : 0 : part = RTE_MIN(len, dlen);
1260 [ # # # # : 0 : rte_memcpy(pdst, psrc, part);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1261 : 0 : copy += part;
1262 : 0 : loc->mbuf_off += part;
1263 : 0 : len -= part;
1264 [ # # # # : 0 : if (!len) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1265 [ # # # # : 0 : if (loc->mbuf_off >= rte_pktmbuf_data_len(loc->mbuf)) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1266 : : loc->mbuf_off = 0;
1267 : : /* Exhausted packet, just free. */
1268 : : mbuf = loc->mbuf;
1269 : 0 : loc->mbuf = mbuf->next;
1270 : : rte_pktmbuf_free_seg(mbuf);
1271 : : loc->mbuf_off = 0;
1272 : : MLX5_ASSERT(loc->mbuf_nseg >= 1);
1273 : 0 : --loc->mbuf_nseg;
1274 : : }
1275 : : return copy;
1276 : : }
1277 : 0 : pdst += part;
1278 : : } while (true);
1279 : : }
1280 : :
1281 : : /**
1282 : : * Build the Ethernet Segment with inlined data from multi-segment packet.
1283 : : * Checks the boundary of WQEBB and ring buffer wrapping, supports Software
1284 : : * Parser, Checksums and VLAN insertion Tx offload features.
1285 : : *
1286 : : * @param txq
1287 : : * Pointer to TX queue structure.
1288 : : * @param loc
1289 : : * Pointer to burst routine local context.
1290 : : * @param wqe
1291 : : * Pointer to WQE to fill with built Ethernet Segment.
1292 : : * @param vlan
1293 : : * Length of VLAN tag insertion if any.
1294 : : * @param inlen
1295 : : * Length of data to inline (VLAN included, if any).
1296 : : * @param tso
1297 : : * TSO flag, set mss field from the packet.
1298 : : * @param olx
1299 : : * Configured Tx offloads mask. It is fully defined at
1300 : : * compile time and may be used for optimization.
1301 : : *
1302 : : * @return
1303 : : * Pointer to the next Data Segment (aligned and possible NOT wrapped
1304 : : * around - caller should do wrapping check on its own).
1305 : : */
1306 : : static __rte_always_inline struct mlx5_wqe_dseg *
1307 : : mlx5_tx_eseg_mdat(struct mlx5_txq_data *__rte_restrict txq,
1308 : : struct mlx5_txq_local *__rte_restrict loc,
1309 : : struct mlx5_wqe *__rte_restrict wqe,
1310 : : unsigned int vlan,
1311 : : unsigned int inlen,
1312 : : unsigned int tso,
1313 : : unsigned int olx)
1314 : : {
1315 : : struct mlx5_wqe_eseg *__rte_restrict es = &wqe->eseg;
1316 : : uint32_t csum;
1317 : : uint8_t *pdst;
1318 : : unsigned int part, tlen = 0;
1319 : :
1320 : : /*
1321 : : * Calculate and set check sum flags first, uint32_t field
1322 : : * in segment may be shared with Software Parser flags.
1323 : : */
1324 : 0 : csum = MLX5_TXOFF_CONFIG(CSUM) ? txq_ol_cksum_to_cs(loc->mbuf) : 0;
1325 : : if (tso) {
1326 : 0 : csum <<= 24;
1327 : 0 : csum |= loc->mbuf->tso_segsz;
1328 [ # # # # : 0 : es->flags = rte_cpu_to_be_32(csum);
# # # # #
# # # # #
# # # # #
# ]
1329 : : } else {
1330 [ # # # # ]: 0 : es->flags = rte_cpu_to_le_32(csum);
1331 : : }
1332 : : /*
1333 : : * Calculate and set Software Parser offsets and flags.
1334 : : * These flags a set for custom UDP and IP tunnel packets.
1335 : : */
1336 : 0 : es->swp_offs = txq_mbuf_to_swp(loc, &es->swp_flags, olx);
1337 : : /* Fill metadata field if needed. */
1338 : 0 : es->metadata = MLX5_TXOFF_CONFIG(METADATA) ?
1339 : 0 : loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
1340 [ # # # # : 0 : rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) :
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1341 : : 0 : 0;
1342 : : MLX5_ASSERT(inlen >= MLX5_ESEG_MIN_INLINE_SIZE);
1343 : 0 : pdst = (uint8_t *)&es->inline_data;
1344 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(VLAN) && vlan) {
# # # # #
# # # # #
# # ]
1345 : : /* Implement VLAN tag insertion as part inline data. */
1346 : : mlx5_tx_mseg_memcpy(pdst, loc,
1347 : : 2 * RTE_ETHER_ADDR_LEN,
1348 : : 2 * RTE_ETHER_ADDR_LEN, olx);
1349 : : pdst += 2 * RTE_ETHER_ADDR_LEN;
1350 [ # # # # : 0 : *(unaligned_uint32_t *)pdst = rte_cpu_to_be_32
# # # # #
# # # # #
# # ]
1351 : : ((RTE_ETHER_TYPE_VLAN << 16) |
1352 : : loc->mbuf->vlan_tci);
1353 : 0 : pdst += sizeof(struct rte_vlan_hdr);
1354 : : tlen += 2 * RTE_ETHER_ADDR_LEN + sizeof(struct rte_vlan_hdr);
1355 : : }
1356 : : MLX5_ASSERT(pdst < (uint8_t *)txq->wqes_end);
1357 : : /*
1358 : : * The WQEBB space availability is checked by caller.
1359 : : * Here we should be aware of WQE ring buffer wraparound only.
1360 : : */
1361 : 0 : part = (uint8_t *)txq->wqes_end - pdst;
1362 : 0 : part = RTE_MIN(part, inlen - tlen);
1363 : : MLX5_ASSERT(part);
1364 : 0 : do {
1365 : : unsigned int copy;
1366 : :
1367 : : /*
1368 : : * Copying may be interrupted inside the routine
1369 : : * if run into no inline hint flag.
1370 : : */
1371 : 0 : copy = tso ? inlen : txq->inlen_mode;
1372 [ # # # # : 0 : copy = tlen >= copy ? 0 : (copy - tlen);
# # # # #
# # # # #
# # # # #
# ]
1373 : : copy = mlx5_tx_mseg_memcpy(pdst, loc, part, copy, olx);
1374 : 0 : tlen += copy;
1375 [ # # # # : 0 : if (likely(inlen <= tlen) || copy < part) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1376 [ # # # # : 0 : es->inline_hdr_sz = rte_cpu_to_be_16(tlen);
# # # # #
# # # # #
# # # # #
# ]
1377 : 0 : pdst += copy;
1378 : 0 : pdst = RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE);
1379 : : return (struct mlx5_wqe_dseg *)pdst;
1380 : : }
1381 : 0 : pdst = (uint8_t *)txq->wqes;
1382 : 0 : part = inlen - tlen;
1383 : : } while (true);
1384 : : }
1385 : :
1386 : : /**
1387 : : * Build the Data Segment of pointer type.
1388 : : *
1389 : : * @param txq
1390 : : * Pointer to TX queue structure.
1391 : : * @param loc
1392 : : * Pointer to burst routine local context.
1393 : : * @param dseg
1394 : : * Pointer to WQE to fill with built Data Segment.
1395 : : * @param buf
1396 : : * Data buffer to point.
1397 : : * @param len
1398 : : * Data buffer length.
1399 : : * @param olx
1400 : : * Configured Tx offloads mask. It is fully defined at
1401 : : * compile time and may be used for optimization.
1402 : : */
1403 : : static __rte_always_inline void
1404 : : mlx5_tx_dseg_ptr(struct mlx5_txq_data *__rte_restrict txq,
1405 : : struct mlx5_txq_local *__rte_restrict loc,
1406 : : struct mlx5_wqe_dseg *__rte_restrict dseg,
1407 : : uint8_t *buf,
1408 : : unsigned int len,
1409 : : unsigned int olx __rte_unused)
1410 : :
1411 : : {
1412 : : MLX5_ASSERT(len);
1413 [ # # # # : 0 : dseg->bcount = rte_cpu_to_be_32(len);
# # # # ]
1414 [ # # # # : 0 : dseg->lkey = mlx5_mr_mb2mr(&txq->mr_ctrl, loc->mbuf);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
1415 : 0 : dseg->pbuf = rte_cpu_to_be_64((uintptr_t)buf);
1416 : : }
1417 : :
1418 : : /**
1419 : : * Build the Data Segment of pointer type or inline if data length is less than
1420 : : * buffer in minimal Data Segment size.
1421 : : *
1422 : : * @param txq
1423 : : * Pointer to TX queue structure.
1424 : : * @param loc
1425 : : * Pointer to burst routine local context.
1426 : : * @param dseg
1427 : : * Pointer to WQE to fill with built Data Segment.
1428 : : * @param buf
1429 : : * Data buffer to point.
1430 : : * @param len
1431 : : * Data buffer length.
1432 : : * @param olx
1433 : : * Configured Tx offloads mask. It is fully defined at
1434 : : * compile time and may be used for optimization.
1435 : : */
1436 : : static __rte_always_inline void
1437 : : mlx5_tx_dseg_iptr(struct mlx5_txq_data *__rte_restrict txq,
1438 : : struct mlx5_txq_local *__rte_restrict loc,
1439 : : struct mlx5_wqe_dseg *__rte_restrict dseg,
1440 : : uint8_t *buf,
1441 : : unsigned int len,
1442 : : unsigned int olx __rte_unused)
1443 : :
1444 : : {
1445 : : uintptr_t dst, src;
1446 : :
1447 : : MLX5_ASSERT(len);
1448 [ # # # # : 0 : if (len > MLX5_DSEG_MIN_INLINE_SIZE) {
# # # # #
# # # # #
# # # # #
# ]
1449 [ # # # # : 0 : dseg->bcount = rte_cpu_to_be_32(len);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1450 [ # # # # : 0 : dseg->lkey = mlx5_mr_mb2mr(&txq->mr_ctrl, loc->mbuf);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1451 : 0 : dseg->pbuf = rte_cpu_to_be_64((uintptr_t)buf);
1452 : :
1453 : 0 : return;
1454 : : }
1455 [ # # # # : 0 : dseg->bcount = rte_cpu_to_be_32(len | MLX5_ETH_WQE_DATA_INLINE);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1456 : : /* Unrolled implementation of generic rte_memcpy. */
1457 : 0 : dst = (uintptr_t)&dseg->inline_data[0];
1458 : 0 : src = (uintptr_t)buf;
1459 [ # # # # : 0 : if (len & 0x08) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1460 : : #ifdef RTE_ARCH_STRICT_ALIGN
1461 : : MLX5_ASSERT(dst == RTE_PTR_ALIGN(dst, sizeof(uint32_t)));
1462 : : *(uint32_t *)dst = *(unaligned_uint32_t *)src;
1463 : : dst += sizeof(uint32_t);
1464 : : src += sizeof(uint32_t);
1465 : : *(uint32_t *)dst = *(unaligned_uint32_t *)src;
1466 : : dst += sizeof(uint32_t);
1467 : : src += sizeof(uint32_t);
1468 : : #else
1469 : 0 : *(uint64_t *)dst = *(unaligned_uint64_t *)src;
1470 : 0 : dst += sizeof(uint64_t);
1471 : 0 : src += sizeof(uint64_t);
1472 : : #endif
1473 : : }
1474 [ # # # # : 0 : if (len & 0x04) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1475 : 0 : *(uint32_t *)dst = *(unaligned_uint32_t *)src;
1476 : 0 : dst += sizeof(uint32_t);
1477 : 0 : src += sizeof(uint32_t);
1478 : : }
1479 [ # # # # : 0 : if (len & 0x02) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1480 : 0 : *(uint16_t *)dst = *(unaligned_uint16_t *)src;
1481 : 0 : dst += sizeof(uint16_t);
1482 : 0 : src += sizeof(uint16_t);
1483 : : }
1484 [ # # # # : 0 : if (len & 0x01)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1485 : 0 : *(uint8_t *)dst = *(uint8_t *)src;
1486 : : }
1487 : :
1488 : : /**
1489 : : * Build the Data Segment of inlined data from single
1490 : : * segment packet, no VLAN insertion.
1491 : : *
1492 : : * @param txq
1493 : : * Pointer to TX queue structure.
1494 : : * @param loc
1495 : : * Pointer to burst routine local context.
1496 : : * @param dseg
1497 : : * Pointer to WQE to fill with built Data Segment.
1498 : : * @param buf
1499 : : * Data buffer to point.
1500 : : * @param len
1501 : : * Data buffer length.
1502 : : * @param olx
1503 : : * Configured Tx offloads mask. It is fully defined at
1504 : : * compile time and may be used for optimization.
1505 : : *
1506 : : * @return
1507 : : * Pointer to the next Data Segment after inlined data.
1508 : : * Ring buffer wraparound check is needed. We do not do it here because it
1509 : : * may not be needed for the last packet in the eMPW session.
1510 : : */
1511 : : static __rte_always_inline struct mlx5_wqe_dseg *
1512 : : mlx5_tx_dseg_empw(struct mlx5_txq_data *__rte_restrict txq,
1513 : : struct mlx5_txq_local *__rte_restrict loc __rte_unused,
1514 : : struct mlx5_wqe_dseg *__rte_restrict dseg,
1515 : : uint8_t *buf,
1516 : : unsigned int len,
1517 : : unsigned int olx __rte_unused)
1518 : : {
1519 : : unsigned int part;
1520 : : uint8_t *pdst;
1521 : :
1522 : : if (!MLX5_TXOFF_CONFIG(MPW)) {
1523 : : /* Store the descriptor byte counter for eMPW sessions. */
1524 [ # # # # : 0 : dseg->bcount = rte_cpu_to_be_32(len | MLX5_ETH_WQE_DATA_INLINE);
# # # # #
# # # #
# ]
1525 : 0 : pdst = &dseg->inline_data[0];
1526 : : } else {
1527 : : /* The entire legacy MPW session counter is stored on close. */
1528 : : pdst = (uint8_t *)dseg;
1529 : : }
1530 : : /*
1531 : : * The WQEBB space availability is checked by caller.
1532 : : * Here we should be aware of WQE ring buffer wraparound only.
1533 : : */
1534 : 0 : part = (uint8_t *)txq->wqes_end - pdst;
1535 : 0 : part = RTE_MIN(part, len);
1536 : : do {
1537 [ # # # # : 0 : rte_memcpy(pdst, buf, part);
# # # # #
# # # #
# ]
1538 : 0 : len -= part;
1539 [ # # # # : 0 : if (likely(!len)) {
# # # # #
# # # #
# ]
1540 : 0 : pdst += part;
1541 : : if (!MLX5_TXOFF_CONFIG(MPW))
1542 : 0 : pdst = RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE);
1543 : : /* Note: no final wraparound check here. */
1544 : : return (struct mlx5_wqe_dseg *)pdst;
1545 : : }
1546 : 0 : pdst = (uint8_t *)txq->wqes;
1547 : 0 : buf += part;
1548 : : part = len;
1549 : : } while (true);
1550 : : }
1551 : :
1552 : : /**
1553 : : * Build the Data Segment of inlined data from single
1554 : : * segment packet with VLAN insertion.
1555 : : *
1556 : : * @param txq
1557 : : * Pointer to TX queue structure.
1558 : : * @param loc
1559 : : * Pointer to burst routine local context.
1560 : : * @param dseg
1561 : : * Pointer to the dseg fill with built Data Segment.
1562 : : * @param buf
1563 : : * Data buffer to point.
1564 : : * @param len
1565 : : * Data buffer length.
1566 : : * @param olx
1567 : : * Configured Tx offloads mask. It is fully defined at
1568 : : * compile time and may be used for optimization.
1569 : : *
1570 : : * @return
1571 : : * Pointer to the next Data Segment after inlined data.
1572 : : * Ring buffer wraparound check is needed.
1573 : : */
1574 : : static __rte_always_inline struct mlx5_wqe_dseg *
1575 : : mlx5_tx_dseg_vlan(struct mlx5_txq_data *__rte_restrict txq,
1576 : : struct mlx5_txq_local *__rte_restrict loc __rte_unused,
1577 : : struct mlx5_wqe_dseg *__rte_restrict dseg,
1578 : : uint8_t *buf,
1579 : : unsigned int len,
1580 : : unsigned int olx __rte_unused)
1581 : :
1582 : : {
1583 : : unsigned int part;
1584 : : uint8_t *pdst;
1585 : :
1586 : : MLX5_ASSERT(len > MLX5_ESEG_MIN_INLINE_SIZE);
1587 : : if (!MLX5_TXOFF_CONFIG(MPW)) {
1588 : : /* Store the descriptor byte counter for eMPW sessions. */
1589 [ # # # # : 0 : dseg->bcount = rte_cpu_to_be_32
# # # # ]
1590 : : ((len + sizeof(struct rte_vlan_hdr)) |
1591 : : MLX5_ETH_WQE_DATA_INLINE);
1592 [ # # # # : 0 : pdst = &dseg->inline_data[0];
# # # # ]
1593 : : } else {
1594 : : /* The entire legacy MPW session counter is stored on close. */
1595 : : pdst = (uint8_t *)dseg;
1596 : : }
1597 : : memcpy(pdst, buf, MLX5_DSEG_MIN_INLINE_SIZE);
1598 : 0 : buf += MLX5_DSEG_MIN_INLINE_SIZE;
1599 : 0 : pdst += MLX5_DSEG_MIN_INLINE_SIZE;
1600 : 0 : len -= MLX5_DSEG_MIN_INLINE_SIZE;
1601 : : /* Insert VLAN ethertype + VLAN tag. Pointer is aligned. */
1602 : : MLX5_ASSERT(pdst == RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE));
1603 [ # # # # : 0 : if (unlikely(pdst >= (uint8_t *)txq->wqes_end))
# # # # ]
1604 : 0 : pdst = (uint8_t *)txq->wqes;
1605 [ # # # # : 0 : *(uint32_t *)pdst = rte_cpu_to_be_32((RTE_ETHER_TYPE_VLAN << 16) |
# # # # ]
1606 : : loc->mbuf->vlan_tci);
1607 : 0 : pdst += sizeof(struct rte_vlan_hdr);
1608 : : /*
1609 : : * The WQEBB space availability is checked by caller.
1610 : : * Here we should be aware of WQE ring buffer wraparound only.
1611 : : */
1612 : 0 : part = (uint8_t *)txq->wqes_end - pdst;
1613 : 0 : part = RTE_MIN(part, len);
1614 : : do {
1615 [ # # # # : 0 : rte_memcpy(pdst, buf, part);
# # # # ]
1616 : 0 : len -= part;
1617 [ # # # # : 0 : if (likely(!len)) {
# # # # ]
1618 : 0 : pdst += part;
1619 : : if (!MLX5_TXOFF_CONFIG(MPW))
1620 : 0 : pdst = RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE);
1621 : : /* Note: no final wraparound check here. */
1622 : : return (struct mlx5_wqe_dseg *)pdst;
1623 : : }
1624 : 0 : pdst = (uint8_t *)txq->wqes;
1625 : 0 : buf += part;
1626 : : part = len;
1627 : : } while (true);
1628 : : }
1629 : :
1630 : : /**
1631 : : * Build the Ethernet Segment with optionally inlined data with
1632 : : * VLAN insertion and following Data Segments (if any) from
1633 : : * multi-segment packet. Used by ordinary send and TSO.
1634 : : *
1635 : : * @param txq
1636 : : * Pointer to TX queue structure.
1637 : : * @param loc
1638 : : * Pointer to burst routine local context.
1639 : : * @param wqe
1640 : : * Pointer to WQE to fill with built Ethernet/Data Segments.
1641 : : * @param vlan
1642 : : * Length of VLAN header to insert, 0 means no VLAN insertion.
1643 : : * @param inlen
1644 : : * Data length to inline. For TSO this parameter specifies exact value,
1645 : : * for ordinary send routine can be aligned by caller to provide better WQE
1646 : : * space saving and data buffer start address alignment.
1647 : : * This length includes VLAN header being inserted.
1648 : : * @param tso
1649 : : * Zero means ordinary send, inlined data can be extended,
1650 : : * otherwise this is TSO, inlined data length is fixed.
1651 : : * @param olx
1652 : : * Configured Tx offloads mask. It is fully defined at
1653 : : * compile time and may be used for optimization.
1654 : : *
1655 : : * @return
1656 : : * Actual size of built WQE in segments.
1657 : : */
1658 : : static __rte_always_inline unsigned int
1659 : : mlx5_tx_mseg_build(struct mlx5_txq_data *__rte_restrict txq,
1660 : : struct mlx5_txq_local *__rte_restrict loc,
1661 : : struct mlx5_wqe *__rte_restrict wqe,
1662 : : unsigned int vlan,
1663 : : unsigned int inlen,
1664 : : unsigned int tso,
1665 : : unsigned int olx __rte_unused)
1666 : : {
1667 : : struct mlx5_wqe_dseg *__rte_restrict dseg;
1668 : : unsigned int ds;
1669 : :
1670 : : MLX5_ASSERT((rte_pktmbuf_pkt_len(loc->mbuf) + vlan) >= inlen);
1671 : : loc->mbuf_nseg = NB_SEGS(loc->mbuf);
1672 : : loc->mbuf_off = 0;
1673 : :
1674 : : dseg = mlx5_tx_eseg_mdat(txq, loc, wqe, vlan, inlen, tso, olx);
1675 [ # # # # : 0 : if (!loc->mbuf_nseg)
# # # # #
# # # # #
# # # # #
# ]
1676 : 0 : goto dseg_done;
1677 : : /*
1678 : : * There are still some mbuf remaining, not inlined.
1679 : : * The first mbuf may be partially inlined and we
1680 : : * must process the possible non-zero data offset.
1681 : : */
1682 [ # # # # : 0 : if (loc->mbuf_off) {
# # # # #
# # # # #
# # # # #
# ]
1683 : : unsigned int dlen;
1684 : : uint8_t *dptr;
1685 : :
1686 : : /*
1687 : : * Exhausted packets must be dropped before.
1688 : : * Non-zero offset means there are some data
1689 : : * remained in the packet.
1690 : : */
1691 : : MLX5_ASSERT(loc->mbuf_off < rte_pktmbuf_data_len(loc->mbuf));
1692 : : MLX5_ASSERT(rte_pktmbuf_data_len(loc->mbuf));
1693 : 0 : dptr = rte_pktmbuf_mtod_offset(loc->mbuf, uint8_t *,
1694 : : loc->mbuf_off);
1695 : 0 : dlen = rte_pktmbuf_data_len(loc->mbuf) - loc->mbuf_off;
1696 : : /*
1697 : : * Build the pointer/minimal Data Segment.
1698 : : * Do ring buffer wrapping check in advance.
1699 : : */
1700 [ # # # # : 0 : if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
# # # # #
# # # # #
# # # # #
# ]
1701 : 0 : dseg = (struct mlx5_wqe_dseg *)txq->wqes;
1702 : : mlx5_tx_dseg_iptr(txq, loc, dseg, dptr, dlen, olx);
1703 : : /* Store the mbuf to be freed on completion. */
1704 : : MLX5_ASSERT(loc->elts_free);
1705 : 0 : txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
1706 : 0 : --loc->elts_free;
1707 : 0 : ++dseg;
1708 [ # # # # : 0 : if (--loc->mbuf_nseg == 0)
# # # # #
# # # # #
# # # # #
# ]
1709 : 0 : goto dseg_done;
1710 : 0 : loc->mbuf = loc->mbuf->next;
1711 : : loc->mbuf_off = 0;
1712 : : }
1713 : : do {
1714 [ # # # # : 0 : if (unlikely(!rte_pktmbuf_data_len(loc->mbuf))) {
# # # # #
# # # # #
# # # # #
# ]
1715 : : struct rte_mbuf *mbuf;
1716 : :
1717 : : /* Zero length segment found, just skip. */
1718 : : mbuf = loc->mbuf;
1719 : 0 : loc->mbuf = loc->mbuf->next;
1720 : : rte_pktmbuf_free_seg(mbuf);
1721 [ # # # # : 0 : if (--loc->mbuf_nseg == 0)
# # # # #
# # # # #
# # # # #
# ]
1722 : : break;
1723 : : } else {
1724 [ # # # # : 0 : if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
# # # # #
# # # # #
# # # # #
# ]
1725 : 0 : dseg = (struct mlx5_wqe_dseg *)txq->wqes;
1726 : 0 : mlx5_tx_dseg_iptr
1727 : : (txq, loc, dseg,
1728 [ # # # # : 0 : rte_pktmbuf_mtod(loc->mbuf, uint8_t *),
# # # # #
# # # # #
# # # # #
# ]
1729 : : rte_pktmbuf_data_len(loc->mbuf), olx);
1730 : : MLX5_ASSERT(loc->elts_free);
1731 : 0 : txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
1732 : 0 : --loc->elts_free;
1733 : 0 : ++dseg;
1734 [ # # # # : 0 : if (--loc->mbuf_nseg == 0)
# # # # #
# # # # #
# # # # #
# ]
1735 : : break;
1736 : 0 : loc->mbuf = loc->mbuf->next;
1737 : : }
1738 : : } while (true);
1739 : :
1740 : 0 : dseg_done:
1741 : : /* Calculate actual segments used from the dseg pointer. */
1742 [ # # # # : 0 : if ((uintptr_t)wqe < (uintptr_t)dseg)
# # # # #
# # # # #
# # # # #
# ]
1743 : 0 : ds = ((uintptr_t)dseg - (uintptr_t)wqe) / MLX5_WSEG_SIZE;
1744 : : else
1745 : 0 : ds = (((uintptr_t)dseg - (uintptr_t)wqe) +
1746 : 0 : txq->wqe_s * MLX5_WQE_SIZE) / MLX5_WSEG_SIZE;
1747 : : return ds;
1748 : : }
1749 : :
1750 : : /**
1751 : : * The routine checks timestamp flag in the current packet,
1752 : : * and push WAIT WQE into the queue if scheduling is required.
1753 : : *
1754 : : * @param txq
1755 : : * Pointer to TX queue structure.
1756 : : * @param loc
1757 : : * Pointer to burst routine local context.
1758 : : * @param elts
1759 : : * Number of free elements in elts buffer to be checked, for zero
1760 : : * value the check is optimized out by compiler.
1761 : : * @param olx
1762 : : * Configured Tx offloads mask. It is fully defined at
1763 : : * compile time and may be used for optimization.
1764 : : *
1765 : : * @return
1766 : : * MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
1767 : : * MLX5_TXCMP_CODE_SINGLE - continue processing with the packet.
1768 : : * MLX5_TXCMP_CODE_MULTI - the WAIT inserted, continue processing.
1769 : : * Local context variables partially updated.
1770 : : */
1771 : : static __rte_always_inline enum mlx5_txcmp_code
1772 : : mlx5_tx_schedule_send(struct mlx5_txq_data *restrict txq,
1773 : : struct mlx5_txq_local *restrict loc,
1774 : : uint16_t elts,
1775 : : unsigned int olx)
1776 : : {
1777 : 0 : if (MLX5_TXOFF_CONFIG(TXPP) &&
1778 [ # # # # : 0 : loc->mbuf->ol_flags & txq->ts_mask) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
1779 : : struct mlx5_dev_ctx_shared *sh;
1780 : : struct mlx5_wqe *wqe;
1781 : : uint64_t ts;
1782 : :
1783 : : /*
1784 : : * Estimate the required space quickly and roughly.
1785 : : * We would like to ensure the packet can be pushed
1786 : : * to the queue and we won't get the orphan WAIT WQE.
1787 : : */
1788 [ # # # # : 0 : if (loc->wqe_free <= MLX5_WQE_SIZE_MAX / MLX5_WQE_SIZE ||
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1789 : : loc->elts_free < elts)
1790 : : return MLX5_TXCMP_CODE_EXIT;
1791 : : /* Convert the timestamp into completion to wait. */
1792 : 0 : ts = *RTE_MBUF_DYNFIELD(loc->mbuf, txq->ts_offset, uint64_t *);
1793 [ # # # # : 0 : if (txq->ts_last && ts < txq->ts_last)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
1794 : 0 : rte_atomic_fetch_add_explicit(&txq->sh->txpp.err_ts_order,
1795 : : 1, rte_memory_order_relaxed);
1796 : 0 : txq->ts_last = ts;
1797 : 0 : wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
1798 : 0 : sh = txq->sh;
1799 [ # # # # : 0 : if (txq->wait_on_time) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1800 : : /* The wait on time capability should be used. */
1801 [ # # # # : 0 : ts -= sh->txpp.skew;
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1802 : : rte_pmd_mlx5_trace_tx_wait(ts);
1803 : : mlx5_tx_cseg_init(txq, loc, wqe,
1804 : : 1 + sizeof(struct mlx5_wqe_wseg) /
1805 : : MLX5_WSEG_SIZE,
1806 : : MLX5_OPCODE_WAIT |
1807 : : MLX5_OPC_MOD_WAIT_TIME << 24, olx);
1808 : : mlx5_tx_wseg_init(txq, loc, wqe, ts, olx);
1809 : : } else {
1810 : : /* Legacy cross-channel operation should be used. */
1811 : : int32_t wci;
1812 : :
1813 : : wci = mlx5_txpp_convert_tx_ts(sh, ts);
1814 [ # # # # : 0 : if (unlikely(wci < 0))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1815 : : return MLX5_TXCMP_CODE_SINGLE;
1816 : : /* Build the WAIT WQE with specified completion. */
1817 : : rte_pmd_mlx5_trace_tx_wait(ts - sh->txpp.skew);
1818 : : mlx5_tx_cseg_init(txq, loc, wqe,
1819 : : 1 + sizeof(struct mlx5_wqe_qseg) /
1820 : : MLX5_WSEG_SIZE,
1821 : : MLX5_OPCODE_WAIT |
1822 : : MLX5_OPC_MOD_WAIT_CQ_PI << 24, olx);
1823 [ # # # # : 0 : mlx5_tx_qseg_init(txq, loc, wqe, wci, olx);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
1824 : : }
1825 : 0 : ++txq->wqe_ci;
1826 : 0 : --loc->wqe_free;
1827 : : return MLX5_TXCMP_CODE_MULTI;
1828 : : }
1829 : : return MLX5_TXCMP_CODE_SINGLE;
1830 : : }
1831 : :
1832 : : /**
1833 : : * Tx one packet function for multi-segment TSO. Supports all
1834 : : * types of Tx offloads, uses MLX5_OPCODE_TSO to build WQEs,
1835 : : * sends one packet per WQE.
1836 : : *
1837 : : * This routine is responsible for storing processed mbuf
1838 : : * into elts ring buffer and update elts_head.
1839 : : *
1840 : : * @param txq
1841 : : * Pointer to TX queue structure.
1842 : : * @param loc
1843 : : * Pointer to burst routine local context.
1844 : : * @param olx
1845 : : * Configured Tx offloads mask. It is fully defined at
1846 : : * compile time and may be used for optimization.
1847 : : *
1848 : : * @return
1849 : : * MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
1850 : : * MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
1851 : : * Local context variables partially updated.
1852 : : */
1853 : : static __rte_always_inline enum mlx5_txcmp_code
1854 : : mlx5_tx_packet_multi_tso(struct mlx5_txq_data *__rte_restrict txq,
1855 : : struct mlx5_txq_local *__rte_restrict loc,
1856 : : unsigned int olx)
1857 : : {
1858 : : struct mlx5_wqe *__rte_restrict wqe;
1859 : : unsigned int ds, dlen, inlen, ntcp, vlan = 0;
1860 : :
1861 : : MLX5_ASSERT(loc->elts_free >= NB_SEGS(loc->mbuf));
1862 : : if (MLX5_TXOFF_CONFIG(TXPP)) {
1863 : : enum mlx5_txcmp_code wret;
1864 : :
1865 : : /* Generate WAIT for scheduling if requested. */
1866 : : wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
1867 : : if (wret == MLX5_TXCMP_CODE_EXIT)
1868 : : return MLX5_TXCMP_CODE_EXIT;
1869 : : if (wret == MLX5_TXCMP_CODE_ERROR)
1870 : : return MLX5_TXCMP_CODE_ERROR;
1871 : : }
1872 : : /*
1873 : : * Calculate data length to be inlined to estimate
1874 : : * the required space in WQE ring buffer.
1875 : : */
1876 : 0 : dlen = rte_pktmbuf_pkt_len(loc->mbuf);
1877 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(VLAN) && loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN)
# # # # #
# ]
1878 : : vlan = sizeof(struct rte_vlan_hdr);
1879 : 0 : inlen = loc->mbuf->l2_len + vlan +
1880 : 0 : loc->mbuf->l3_len + loc->mbuf->l4_len;
1881 [ # # # # : 0 : if (unlikely((!inlen || !loc->mbuf->tso_segsz)))
# # # # #
# # # # #
# # # # #
# # # #
# ]
1882 : : return MLX5_TXCMP_CODE_ERROR;
1883 [ # # # # : 0 : if (loc->mbuf->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK)
# # # # #
# # # ]
1884 : 0 : inlen += loc->mbuf->outer_l2_len + loc->mbuf->outer_l3_len;
1885 : : /* Packet must contain all TSO headers. */
1886 [ # # # # : 0 : if (unlikely(inlen > MLX5_MAX_TSO_HEADER ||
# # # # #
# # # # #
# # # # #
# # # #
# ]
1887 : : inlen <= MLX5_ESEG_MIN_INLINE_SIZE ||
1888 : : inlen > (dlen + vlan)))
1889 : : return MLX5_TXCMP_CODE_ERROR;
1890 : : /*
1891 : : * Check whether there are enough free WQEBBs:
1892 : : * - Control Segment
1893 : : * - Ethernet Segment
1894 : : * - First Segment of inlined Ethernet data
1895 : : * - ... data continued ...
1896 : : * - Data Segments of pointer/min inline type
1897 : : */
1898 : 0 : ds = NB_SEGS(loc->mbuf) + 2 + (inlen -
1899 : : MLX5_ESEG_MIN_INLINE_SIZE +
1900 : : MLX5_WSEG_SIZE +
1901 : 0 : MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
1902 [ # # # # : 0 : if (unlikely(loc->wqe_free < ((ds + 3) / 4)))
# # # # #
# # # ]
1903 : : return MLX5_TXCMP_CODE_EXIT;
1904 : : /* Check for maximal WQE size. */
1905 [ # # # # : 0 : if (unlikely((MLX5_WQE_SIZE_MAX / MLX5_WSEG_SIZE) < ds))
# # # # #
# # # ]
1906 : : return MLX5_TXCMP_CODE_ERROR;
1907 : : #ifdef MLX5_PMD_SOFT_COUNTERS
1908 : : /* Update sent data bytes/packets counters. */
1909 : 0 : ntcp = (dlen - (inlen - vlan) + loc->mbuf->tso_segsz - 1) /
1910 : : loc->mbuf->tso_segsz;
1911 : : /*
1912 : : * One will be added for mbuf itself at the end of the mlx5_tx_burst
1913 : : * from loc->pkts_sent field.
1914 : : */
1915 : 0 : --ntcp;
1916 : 0 : txq->stats.opackets += ntcp;
1917 : 0 : txq->stats.obytes += dlen + vlan + ntcp * inlen;
1918 : : #endif
1919 [ # # # # : 0 : wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
# # # # #
# # # ]
1920 : : loc->wqe_last = wqe;
1921 : : mlx5_tx_cseg_init(txq, loc, wqe, 0, MLX5_OPCODE_TSO, olx);
1922 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
1923 : : ds = mlx5_tx_mseg_build(txq, loc, wqe, vlan, inlen, 1, olx);
1924 [ # # # # : 0 : wqe->cseg.sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | ds);
# # # # #
# # # ]
1925 : 0 : txq->wqe_ci += (ds + 3) / 4;
1926 : 0 : loc->wqe_free -= (ds + 3) / 4;
1927 : : return MLX5_TXCMP_CODE_MULTI;
1928 : : }
1929 : :
1930 : : /**
1931 : : * Tx one packet function for multi-segment SEND. Supports all types of Tx
1932 : : * offloads, uses MLX5_OPCODE_SEND to build WQEs, sends one packet per WQE,
1933 : : * without any data inlining in Ethernet Segment.
1934 : : *
1935 : : * This routine is responsible for storing processed mbuf
1936 : : * into elts ring buffer and update elts_head.
1937 : : *
1938 : : * @param txq
1939 : : * Pointer to TX queue structure.
1940 : : * @param loc
1941 : : * Pointer to burst routine local context.
1942 : : * @param olx
1943 : : * Configured Tx offloads mask. It is fully defined at
1944 : : * compile time and may be used for optimization.
1945 : : *
1946 : : * @return
1947 : : * MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
1948 : : * MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
1949 : : * Local context variables partially updated.
1950 : : */
1951 : : static __rte_always_inline enum mlx5_txcmp_code
1952 : : mlx5_tx_packet_multi_send(struct mlx5_txq_data *__rte_restrict txq,
1953 : : struct mlx5_txq_local *__rte_restrict loc,
1954 : : unsigned int olx)
1955 : : {
1956 : : struct mlx5_wqe_dseg *__rte_restrict dseg;
1957 : : struct mlx5_wqe *__rte_restrict wqe;
1958 : : unsigned int ds, nseg;
1959 : :
1960 : : MLX5_ASSERT(NB_SEGS(loc->mbuf) > 1);
1961 : : MLX5_ASSERT(loc->elts_free >= NB_SEGS(loc->mbuf));
1962 : : if (MLX5_TXOFF_CONFIG(TXPP)) {
1963 : : enum mlx5_txcmp_code wret;
1964 : :
1965 : : /* Generate WAIT for scheduling if requested. */
1966 : : wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
1967 : : if (wret == MLX5_TXCMP_CODE_EXIT)
1968 : : return MLX5_TXCMP_CODE_EXIT;
1969 : : if (wret == MLX5_TXCMP_CODE_ERROR)
1970 : : return MLX5_TXCMP_CODE_ERROR;
1971 : : }
1972 : : /*
1973 : : * No inline at all, it means the CPU cycles saving is prioritized at
1974 : : * configuration, we should not copy any packet data to WQE.
1975 : : */
1976 : 0 : nseg = NB_SEGS(loc->mbuf);
1977 : 0 : ds = 2 + nseg;
1978 [ # # # # : 0 : if (unlikely(loc->wqe_free < ((ds + 3) / 4)))
# # # # #
# # # ]
1979 : : return MLX5_TXCMP_CODE_EXIT;
1980 : : /* Check for maximal WQE size. */
1981 [ # # # # : 0 : if (unlikely((MLX5_WQE_SIZE_MAX / MLX5_WSEG_SIZE) < ds))
# # # # #
# # # ]
1982 : : return MLX5_TXCMP_CODE_ERROR;
1983 : : /*
1984 : : * Some Tx offloads may cause an error if packet is not long enough,
1985 : : * check against assumed minimal length.
1986 : : */
1987 [ # # # # : 0 : if (rte_pktmbuf_pkt_len(loc->mbuf) <= MLX5_ESEG_MIN_INLINE_SIZE)
# # # # #
# # # ]
1988 : : return MLX5_TXCMP_CODE_ERROR;
1989 : : #ifdef MLX5_PMD_SOFT_COUNTERS
1990 : : /* Update sent data bytes counter. */
1991 : 0 : txq->stats.obytes += rte_pktmbuf_pkt_len(loc->mbuf);
1992 [ # # # # ]: 0 : if (MLX5_TXOFF_CONFIG(VLAN) &&
1993 [ # # # # : 0 : loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN)
# # # # #
# ]
1994 : 0 : txq->stats.obytes += sizeof(struct rte_vlan_hdr);
1995 : : #endif
1996 : : /*
1997 : : * SEND WQE, one WQEBB:
1998 : : * - Control Segment, SEND opcode
1999 : : * - Ethernet Segment, optional VLAN, no inline
2000 : : * - Data Segments, pointer only type
2001 : : */
2002 [ # # # # : 0 : wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
# # # # #
# # # ]
2003 : : loc->wqe_last = wqe;
2004 : : mlx5_tx_cseg_init(txq, loc, wqe, ds, MLX5_OPCODE_SEND, olx);
2005 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
2006 : : mlx5_tx_eseg_none(txq, loc, wqe, olx);
2007 : 0 : dseg = &wqe->dseg[0];
2008 : : do {
2009 [ # # # # : 0 : if (unlikely(!rte_pktmbuf_data_len(loc->mbuf))) {
# # # # #
# # # ]
2010 : : struct rte_mbuf *mbuf;
2011 : :
2012 : : /*
2013 : : * Zero length segment found, have to correct total
2014 : : * size of WQE in segments.
2015 : : * It is supposed to be rare occasion, so in normal
2016 : : * case (no zero length segments) we avoid extra
2017 : : * writing to the Control Segment.
2018 : : */
2019 : 0 : --ds;
2020 : 0 : wqe->cseg.sq_ds -= RTE_BE32(1);
2021 : : mbuf = loc->mbuf;
2022 [ # # ]: 0 : loc->mbuf = mbuf->next;
2023 : : rte_pktmbuf_free_seg(mbuf);
2024 [ # # # # : 0 : if (--nseg == 0)
# # # # #
# # # ]
2025 : : break;
2026 : : } else {
2027 : 0 : mlx5_tx_dseg_ptr
2028 : : (txq, loc, dseg,
2029 [ # # # # : 0 : rte_pktmbuf_mtod(loc->mbuf, uint8_t *),
# # # # #
# # # ]
2030 : : rte_pktmbuf_data_len(loc->mbuf), olx);
2031 : 0 : txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
2032 : 0 : --loc->elts_free;
2033 [ # # # # : 0 : if (--nseg == 0)
# # # # #
# # # ]
2034 : : break;
2035 : 0 : ++dseg;
2036 [ # # # # : 0 : if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
# # # # #
# # # ]
2037 : 0 : dseg = (struct mlx5_wqe_dseg *)txq->wqes;
2038 : 0 : loc->mbuf = loc->mbuf->next;
2039 : : }
2040 : : } while (true);
2041 : 0 : txq->wqe_ci += (ds + 3) / 4;
2042 : 0 : loc->wqe_free -= (ds + 3) / 4;
2043 : : return MLX5_TXCMP_CODE_MULTI;
2044 : : }
2045 : :
2046 : : /**
2047 : : * Tx one packet function for multi-segment SEND. Supports all
2048 : : * types of Tx offloads, uses MLX5_OPCODE_SEND to build WQEs,
2049 : : * sends one packet per WQE, with data inlining in
2050 : : * Ethernet Segment and minimal Data Segments.
2051 : : *
2052 : : * This routine is responsible for storing processed mbuf
2053 : : * into elts ring buffer and update elts_head.
2054 : : *
2055 : : * @param txq
2056 : : * Pointer to TX queue structure.
2057 : : * @param loc
2058 : : * Pointer to burst routine local context.
2059 : : * @param olx
2060 : : * Configured Tx offloads mask. It is fully defined at
2061 : : * compile time and may be used for optimization.
2062 : : *
2063 : : * @return
2064 : : * MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
2065 : : * MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
2066 : : * Local context variables partially updated.
2067 : : */
2068 : : static __rte_always_inline enum mlx5_txcmp_code
2069 : : mlx5_tx_packet_multi_inline(struct mlx5_txq_data *__rte_restrict txq,
2070 : : struct mlx5_txq_local *__rte_restrict loc,
2071 : : unsigned int olx)
2072 : : {
2073 : : struct mlx5_wqe *__rte_restrict wqe;
2074 : : unsigned int ds, inlen, dlen, vlan = 0;
2075 : :
2076 : : MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
2077 : : MLX5_ASSERT(NB_SEGS(loc->mbuf) > 1);
2078 : : MLX5_ASSERT(loc->elts_free >= NB_SEGS(loc->mbuf));
2079 : : /*
2080 : : * First calculate data length to be inlined
2081 : : * to estimate the required space for WQE.
2082 : : */
2083 : 0 : dlen = rte_pktmbuf_pkt_len(loc->mbuf);
2084 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(VLAN) && loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN)
# # ]
2085 : : vlan = sizeof(struct rte_vlan_hdr);
2086 : 0 : inlen = dlen + vlan;
2087 : : /* Check against minimal length. */
2088 [ # # # # : 0 : if (inlen <= MLX5_ESEG_MIN_INLINE_SIZE)
# # # # ]
2089 : : return MLX5_TXCMP_CODE_ERROR;
2090 : : MLX5_ASSERT(txq->inlen_send >= MLX5_ESEG_MIN_INLINE_SIZE);
2091 [ # # # # : 0 : if (inlen > txq->inlen_send ||
# # # # ]
2092 [ # # # # : 0 : loc->mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE) {
# # # # ]
2093 : : struct rte_mbuf *mbuf;
2094 : : unsigned int nxlen;
2095 : : uintptr_t start;
2096 : :
2097 : : mbuf = loc->mbuf;
2098 : 0 : nxlen = rte_pktmbuf_data_len(mbuf) + vlan;
2099 : : /*
2100 : : * Packet length exceeds the allowed inline data length,
2101 : : * check whether the minimal inlining is required.
2102 : : */
2103 [ # # # # : 0 : if (txq->inlen_mode) {
# # # # ]
2104 : : MLX5_ASSERT(txq->inlen_mode >=
2105 : : MLX5_ESEG_MIN_INLINE_SIZE);
2106 : : MLX5_ASSERT(txq->inlen_mode <= txq->inlen_send);
2107 : 0 : inlen = RTE_MIN(txq->inlen_mode, inlen);
2108 [ # # # # : 0 : } else if (vlan && !txq->vlan_en) {
# # # # #
# # # ]
2109 : : /*
2110 : : * VLAN insertion is requested and hardware does not
2111 : : * support the offload, will do with software inline.
2112 : : */
2113 : : inlen = MLX5_ESEG_MIN_INLINE_SIZE;
2114 [ # # # # : 0 : } else if (mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE ||
# # # # #
# # # # #
# # ]
2115 : : nxlen > txq->inlen_send) {
2116 : : return mlx5_tx_packet_multi_send(txq, loc, olx);
2117 [ # # # # : 0 : } else if (nxlen <= MLX5_ESEG_MIN_INLINE_SIZE) {
# # # # ]
2118 : : inlen = MLX5_ESEG_MIN_INLINE_SIZE;
2119 : : } else {
2120 : 0 : goto do_first;
2121 : : }
2122 [ # # # # : 0 : if (mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE)
# # # # ]
2123 : 0 : goto do_build;
2124 : : /*
2125 : : * Now we know the minimal amount of data is requested
2126 : : * to inline. Check whether we should inline the buffers
2127 : : * from the chain beginning to eliminate some mbufs.
2128 : : */
2129 [ # # # # : 0 : if (unlikely(nxlen <= txq->inlen_send)) {
# # # # ]
2130 : : /* We can inline first mbuf at least. */
2131 [ # # # # : 0 : if (nxlen < inlen) {
# # # # ]
2132 : : unsigned int smlen;
2133 : :
2134 : : /* Scan mbufs till inlen filled. */
2135 : : do {
2136 : : smlen = nxlen;
2137 : 0 : mbuf = NEXT(mbuf);
2138 : : MLX5_ASSERT(mbuf);
2139 : 0 : nxlen = rte_pktmbuf_data_len(mbuf);
2140 : 0 : nxlen += smlen;
2141 [ # # # # : 0 : } while (unlikely(nxlen < inlen));
# # # # ]
2142 [ # # # # : 0 : if (unlikely(nxlen > txq->inlen_send)) {
# # # # ]
2143 : : /* We cannot inline entire mbuf. */
2144 : 0 : smlen = inlen - smlen;
2145 : 0 : start = rte_pktmbuf_mtod_offset
2146 : : (mbuf, uintptr_t, smlen);
2147 : 0 : goto do_align;
2148 : : }
2149 : : }
2150 : 0 : do_first:
2151 : : do {
2152 : : inlen = nxlen;
2153 : 0 : mbuf = NEXT(mbuf);
2154 : : /* There should be not end of packet. */
2155 : : MLX5_ASSERT(mbuf);
2156 [ # # # # : 0 : if (mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE)
# # # # ]
2157 : : break;
2158 : 0 : nxlen = inlen + rte_pktmbuf_data_len(mbuf);
2159 [ # # # # : 0 : } while (unlikely(nxlen < txq->inlen_send));
# # # # ]
2160 : : }
2161 : 0 : start = rte_pktmbuf_mtod(mbuf, uintptr_t);
2162 : : /*
2163 : : * Check whether we can do inline to align start
2164 : : * address of data buffer to cacheline.
2165 : : */
2166 : 0 : do_align:
2167 : 0 : start = (~start + 1) & (RTE_CACHE_LINE_SIZE - 1);
2168 [ # # # # : 0 : if (unlikely(start)) {
# # # # ]
2169 : 0 : start += inlen;
2170 [ # # # # : 0 : if (start <= txq->inlen_send)
# # # # ]
2171 : 0 : inlen = start;
2172 : : }
2173 : : }
2174 : : /*
2175 : : * Check whether there are enough free WQEBBs:
2176 : : * - Control Segment
2177 : : * - Ethernet Segment
2178 : : * - First Segment of inlined Ethernet data
2179 : : * - ... data continued ...
2180 : : * - Data Segments of pointer/min inline type
2181 : : *
2182 : : * Estimate the number of Data Segments conservatively,
2183 : : * supposing no any mbufs is being freed during inlining.
2184 : : */
2185 [ # # # # : 0 : do_build:
# # # # ]
2186 : : if (MLX5_TXOFF_CONFIG(TXPP)) {
2187 : : enum mlx5_txcmp_code wret;
2188 : :
2189 : : /* Generate WAIT for scheduling if requested. */
2190 : : wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
2191 : : if (wret == MLX5_TXCMP_CODE_EXIT)
2192 : : return MLX5_TXCMP_CODE_EXIT;
2193 : : if (wret == MLX5_TXCMP_CODE_ERROR)
2194 : : return MLX5_TXCMP_CODE_ERROR;
2195 : : }
2196 : : MLX5_ASSERT(inlen <= txq->inlen_send);
2197 : 0 : ds = NB_SEGS(loc->mbuf) + 2 + (inlen -
2198 : : MLX5_ESEG_MIN_INLINE_SIZE +
2199 : : MLX5_WSEG_SIZE +
2200 : 0 : MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
2201 [ # # # # : 0 : if (unlikely(loc->wqe_free < ((ds + 3) / 4)))
# # # # ]
2202 : : return MLX5_TXCMP_CODE_EXIT;
2203 : : /* Check for maximal WQE size. */
2204 [ # # # # : 0 : if (unlikely((MLX5_WQE_SIZE_MAX / MLX5_WSEG_SIZE) < ds)) {
# # # # ]
2205 : : /* Check if we can adjust the inline length. */
2206 [ # # # # : 0 : if (unlikely(txq->inlen_mode)) {
# # # # ]
2207 : 0 : ds = NB_SEGS(loc->mbuf) + 2 +
2208 : 0 : (txq->inlen_mode -
2209 : : MLX5_ESEG_MIN_INLINE_SIZE +
2210 : : MLX5_WSEG_SIZE +
2211 : 0 : MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
2212 [ # # # # : 0 : if (unlikely((MLX5_WQE_SIZE_MAX / MLX5_WSEG_SIZE) < ds))
# # # # ]
2213 : : return MLX5_TXCMP_CODE_ERROR;
2214 : : }
2215 : : /* We have lucky opportunity to adjust. */
2216 : 0 : inlen = RTE_MIN(inlen, MLX5_WQE_SIZE_MAX -
2217 : : MLX5_WSEG_SIZE * 2 -
2218 : : MLX5_WSEG_SIZE * NB_SEGS(loc->mbuf) -
2219 : : MLX5_WSEG_SIZE +
2220 : : MLX5_ESEG_MIN_INLINE_SIZE);
2221 : : }
2222 : : #ifdef MLX5_PMD_SOFT_COUNTERS
2223 : : /* Update sent data bytes/packets counters. */
2224 : 0 : txq->stats.obytes += dlen + vlan;
2225 : : #endif
2226 [ # # # # : 0 : wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
# # # # ]
2227 : : loc->wqe_last = wqe;
2228 : : mlx5_tx_cseg_init(txq, loc, wqe, 0, MLX5_OPCODE_SEND, olx);
2229 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
2230 : : ds = mlx5_tx_mseg_build(txq, loc, wqe, vlan, inlen, 0, olx);
2231 [ # # # # : 0 : wqe->cseg.sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | ds);
# # # # ]
2232 : 0 : txq->wqe_ci += (ds + 3) / 4;
2233 : 0 : loc->wqe_free -= (ds + 3) / 4;
2234 : : return MLX5_TXCMP_CODE_MULTI;
2235 : : }
2236 : :
2237 : : /**
2238 : : * Tx burst function for multi-segment packets. Supports all
2239 : : * types of Tx offloads, uses MLX5_OPCODE_SEND/TSO to build WQEs,
2240 : : * sends one packet per WQE. Function stops sending if it
2241 : : * encounters the single-segment packet.
2242 : : *
2243 : : * This routine is responsible for storing processed mbuf
2244 : : * into elts ring buffer and update elts_head.
2245 : : *
2246 : : * @param txq
2247 : : * Pointer to TX queue structure.
2248 : : * @param[in] pkts
2249 : : * Packets to transmit.
2250 : : * @param pkts_n
2251 : : * Number of packets in array.
2252 : : * @param loc
2253 : : * Pointer to burst routine local context.
2254 : : * @param olx
2255 : : * Configured Tx offloads mask. It is fully defined at
2256 : : * compile time and may be used for optimization.
2257 : : *
2258 : : * @return
2259 : : * MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
2260 : : * MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
2261 : : * MLX5_TXCMP_CODE_SINGLE - single-segment packet encountered.
2262 : : * MLX5_TXCMP_CODE_TSO - TSO single-segment packet encountered.
2263 : : * Local context variables updated.
2264 : : */
2265 : : static __rte_always_inline enum mlx5_txcmp_code
2266 : : mlx5_tx_burst_mseg(struct mlx5_txq_data *__rte_restrict txq,
2267 : : struct rte_mbuf **__rte_restrict pkts,
2268 : : unsigned int pkts_n,
2269 : : struct mlx5_txq_local *__rte_restrict loc,
2270 : : unsigned int olx)
2271 : : {
2272 : : MLX5_ASSERT(loc->elts_free && loc->wqe_free);
2273 : : MLX5_ASSERT(pkts_n > loc->pkts_sent);
2274 : 0 : pkts += loc->pkts_sent + 1;
2275 : 0 : pkts_n -= loc->pkts_sent;
2276 : 0 : for (;;) {
2277 : : enum mlx5_txcmp_code ret;
2278 : :
2279 : : MLX5_ASSERT(NB_SEGS(loc->mbuf) > 1);
2280 : : /*
2281 : : * Estimate the number of free elts quickly but conservatively.
2282 : : * Some segment may be fully inlined and freed,
2283 : : * ignore this here - precise estimation is costly.
2284 : : */
2285 [ # # # # : 0 : if (loc->elts_free < NB_SEGS(loc->mbuf))
# # # # #
# # # ]
2286 : : return MLX5_TXCMP_CODE_EXIT;
2287 : 0 : if (MLX5_TXOFF_CONFIG(TSO) &&
2288 [ # # # # : 0 : unlikely(loc->mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG)) {
# # # # #
# # # ]
2289 : : /* Proceed with multi-segment TSO. */
2290 : : ret = mlx5_tx_packet_multi_tso(txq, loc, olx);
2291 : : } else if (MLX5_TXOFF_CONFIG(INLINE)) {
2292 : : /* Proceed with multi-segment SEND with inlining. */
2293 : : ret = mlx5_tx_packet_multi_inline(txq, loc, olx);
2294 : : } else {
2295 : : /* Proceed with multi-segment SEND w/o inlining. */
2296 : : ret = mlx5_tx_packet_multi_send(txq, loc, olx);
2297 : : }
2298 : : if (ret == MLX5_TXCMP_CODE_EXIT)
2299 : : return MLX5_TXCMP_CODE_EXIT;
2300 : : if (ret == MLX5_TXCMP_CODE_ERROR)
2301 : : return MLX5_TXCMP_CODE_ERROR;
2302 : : /* WQE is built, go to the next packet. */
2303 : 0 : ++loc->pkts_sent;
2304 : 0 : --pkts_n;
2305 [ # # # # : 0 : if (unlikely(!pkts_n || !loc->elts_free || !loc->wqe_free))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
2306 : : return MLX5_TXCMP_CODE_EXIT;
2307 : 0 : loc->mbuf = *pkts++;
2308 [ # # # # : 0 : if (pkts_n > 1)
# # # # #
# # # ]
2309 : 0 : rte_prefetch0(*pkts);
2310 [ # # # # : 0 : if (likely(NB_SEGS(loc->mbuf) > 1))
# # # # #
# # # ]
2311 : : continue;
2312 : : /* Here ends the series of multi-segment packets. */
2313 : 0 : if (MLX5_TXOFF_CONFIG(TSO) &&
2314 [ # # # # : 0 : unlikely(loc->mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG))
# # # # #
# # # ]
2315 : 0 : return MLX5_TXCMP_CODE_TSO;
2316 : : return MLX5_TXCMP_CODE_SINGLE;
2317 : : }
2318 : : MLX5_ASSERT(false);
2319 : : }
2320 : :
2321 : : /**
2322 : : * Tx burst function for single-segment packets with TSO.
2323 : : * Supports all types of Tx offloads, except multi-packets.
2324 : : * Uses MLX5_OPCODE_TSO to build WQEs, sends one packet per WQE.
2325 : : * Function stops sending if it encounters the multi-segment
2326 : : * packet or packet without TSO requested.
2327 : : *
2328 : : * The routine is responsible for storing processed mbuf into elts ring buffer
2329 : : * and update elts_head if inline offloads is requested due to possible early
2330 : : * freeing of the inlined mbufs (can not store pkts array in elts as a batch).
2331 : : *
2332 : : * @param txq
2333 : : * Pointer to TX queue structure.
2334 : : * @param[in] pkts
2335 : : * Packets to transmit.
2336 : : * @param pkts_n
2337 : : * Number of packets in array.
2338 : : * @param loc
2339 : : * Pointer to burst routine local context.
2340 : : * @param olx
2341 : : * Configured Tx offloads mask. It is fully defined at
2342 : : * compile time and may be used for optimization.
2343 : : *
2344 : : * @return
2345 : : * MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
2346 : : * MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
2347 : : * MLX5_TXCMP_CODE_SINGLE - single-segment packet encountered.
2348 : : * MLX5_TXCMP_CODE_MULTI - multi-segment packet encountered.
2349 : : * Local context variables updated.
2350 : : */
2351 : : static __rte_always_inline enum mlx5_txcmp_code
2352 : : mlx5_tx_burst_tso(struct mlx5_txq_data *__rte_restrict txq,
2353 : : struct rte_mbuf **__rte_restrict pkts,
2354 : : unsigned int pkts_n,
2355 : : struct mlx5_txq_local *__rte_restrict loc,
2356 : : unsigned int olx)
2357 : : {
2358 : : MLX5_ASSERT(loc->elts_free && loc->wqe_free);
2359 : : MLX5_ASSERT(pkts_n > loc->pkts_sent);
2360 : 0 : pkts += loc->pkts_sent + 1;
2361 : 0 : pkts_n -= loc->pkts_sent;
2362 : : for (;;) {
2363 : : struct mlx5_wqe_dseg *__rte_restrict dseg;
2364 : : struct mlx5_wqe *__rte_restrict wqe;
2365 : : unsigned int ds, dlen, hlen, ntcp, vlan = 0;
2366 : : uint8_t *dptr;
2367 : :
2368 : : MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
2369 : : if (MLX5_TXOFF_CONFIG(TXPP)) {
2370 : : enum mlx5_txcmp_code wret;
2371 : :
2372 : : /* Generate WAIT for scheduling if requested. */
2373 : : wret = mlx5_tx_schedule_send(txq, loc, 1, olx);
2374 : : if (wret == MLX5_TXCMP_CODE_EXIT)
2375 : : return MLX5_TXCMP_CODE_EXIT;
2376 : : if (wret == MLX5_TXCMP_CODE_ERROR)
2377 : : return MLX5_TXCMP_CODE_ERROR;
2378 : : }
2379 : 0 : dlen = rte_pktmbuf_data_len(loc->mbuf);
2380 : 0 : if (MLX5_TXOFF_CONFIG(VLAN) &&
2381 [ # # # # : 0 : loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) {
# # # # #
# ]
2382 : : vlan = sizeof(struct rte_vlan_hdr);
2383 : : }
2384 : : /*
2385 : : * First calculate the WQE size to check
2386 : : * whether we have enough space in ring buffer.
2387 : : */
2388 : 0 : hlen = loc->mbuf->l2_len + vlan +
2389 : 0 : loc->mbuf->l3_len + loc->mbuf->l4_len;
2390 [ # # # # : 0 : if (unlikely((!hlen || !loc->mbuf->tso_segsz)))
# # # # #
# # # # #
# # # # #
# # # #
# ]
2391 : : return MLX5_TXCMP_CODE_ERROR;
2392 [ # # # # : 0 : if (loc->mbuf->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK)
# # # # #
# # # ]
2393 : 0 : hlen += loc->mbuf->outer_l2_len +
2394 : 0 : loc->mbuf->outer_l3_len;
2395 : : /* Segment must contain all TSO headers. */
2396 [ # # # # : 0 : if (unlikely(hlen > MLX5_MAX_TSO_HEADER ||
# # # # #
# # # # #
# # # # #
# # # #
# ]
2397 : : hlen <= MLX5_ESEG_MIN_INLINE_SIZE ||
2398 : : hlen > (dlen + vlan)))
2399 : : return MLX5_TXCMP_CODE_ERROR;
2400 : : /*
2401 : : * Check whether there are enough free WQEBBs:
2402 : : * - Control Segment
2403 : : * - Ethernet Segment
2404 : : * - First Segment of inlined Ethernet data
2405 : : * - ... data continued ...
2406 : : * - Finishing Data Segment of pointer type
2407 : : */
2408 : 0 : ds = 4 + (hlen - MLX5_ESEG_MIN_INLINE_SIZE +
2409 : 0 : MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
2410 [ # # # # : 0 : if (loc->wqe_free < ((ds + 3) / 4))
# # # # #
# # # ]
2411 : : return MLX5_TXCMP_CODE_EXIT;
2412 : : #ifdef MLX5_PMD_SOFT_COUNTERS
2413 : : /* Update sent data bytes/packets counters. */
2414 : 0 : ntcp = (dlen + vlan - hlen +
2415 : 0 : loc->mbuf->tso_segsz - 1) /
2416 : : loc->mbuf->tso_segsz;
2417 : : /*
2418 : : * One will be added for mbuf itself at the end
2419 : : * of the mlx5_tx_burst from loc->pkts_sent field.
2420 : : */
2421 : 0 : --ntcp;
2422 : 0 : txq->stats.opackets += ntcp;
2423 : 0 : txq->stats.obytes += dlen + vlan + ntcp * hlen;
2424 : : #endif
2425 : : /*
2426 : : * Build the TSO WQE:
2427 : : * - Control Segment
2428 : : * - Ethernet Segment with hlen bytes inlined
2429 : : * - Data Segment of pointer type
2430 : : */
2431 [ # # # # : 0 : wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
# # # # #
# # # ]
2432 : : loc->wqe_last = wqe;
2433 : : mlx5_tx_cseg_init(txq, loc, wqe, ds, MLX5_OPCODE_TSO, olx);
2434 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
2435 : : dseg = mlx5_tx_eseg_data(txq, loc, wqe, vlan, hlen, 1, olx);
2436 [ # # # # : 0 : dptr = rte_pktmbuf_mtod(loc->mbuf, uint8_t *) + hlen - vlan;
# # ]
2437 [ # # # # : 0 : dlen -= hlen - vlan;
# # # # #
# ]
2438 : : mlx5_tx_dseg_ptr(txq, loc, dseg, dptr, dlen, olx);
2439 : : /*
2440 : : * WQE is built, update the loop parameters
2441 : : * and go to the next packet.
2442 : : */
2443 : 0 : txq->wqe_ci += (ds + 3) / 4;
2444 : 0 : loc->wqe_free -= (ds + 3) / 4;
2445 : : if (MLX5_TXOFF_CONFIG(INLINE))
2446 : 0 : txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
2447 : 0 : --loc->elts_free;
2448 : 0 : ++loc->pkts_sent;
2449 : 0 : --pkts_n;
2450 [ # # # # : 0 : if (unlikely(!pkts_n || !loc->elts_free || !loc->wqe_free))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
2451 : : return MLX5_TXCMP_CODE_EXIT;
2452 : 0 : loc->mbuf = *pkts++;
2453 [ # # # # : 0 : if (pkts_n > 1)
# # # # #
# # # ]
2454 : 0 : rte_prefetch0(*pkts);
2455 : 0 : if (MLX5_TXOFF_CONFIG(MULTI) &&
2456 [ # # # # : 0 : unlikely(NB_SEGS(loc->mbuf) > 1))
# # # # #
# # # ]
2457 : : return MLX5_TXCMP_CODE_MULTI;
2458 [ # # # # : 0 : if (likely(!(loc->mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG)))
# # # # #
# # # ]
2459 : : return MLX5_TXCMP_CODE_SINGLE;
2460 : : /* Continue with the next TSO packet. */
2461 : : }
2462 : : MLX5_ASSERT(false);
2463 : : }
2464 : :
2465 : : /**
2466 : : * Analyze the packet and select the best method to send.
2467 : : *
2468 : : * @param txq
2469 : : * Pointer to TX queue structure.
2470 : : * @param loc
2471 : : * Pointer to burst routine local context.
2472 : : * @param olx
2473 : : * Configured Tx offloads mask. It is fully defined at
2474 : : * compile time and may be used for optimization.
2475 : : * @param newp
2476 : : * The predefined flag whether do complete check for
2477 : : * multi-segment packets and TSO.
2478 : : *
2479 : : * @return
2480 : : * MLX5_TXCMP_CODE_MULTI - multi-segment packet encountered.
2481 : : * MLX5_TXCMP_CODE_TSO - TSO required, use TSO/LSO.
2482 : : * MLX5_TXCMP_CODE_SINGLE - single-segment packet, use SEND.
2483 : : * MLX5_TXCMP_CODE_EMPW - single-segment packet, use MPW.
2484 : : */
2485 : : static __rte_always_inline enum mlx5_txcmp_code
2486 : : mlx5_tx_able_to_empw(struct mlx5_txq_data *__rte_restrict txq,
2487 : : struct mlx5_txq_local *__rte_restrict loc,
2488 : : unsigned int olx,
2489 : : bool newp)
2490 : : {
2491 : : /* Check for multi-segment packet. */
2492 : : if (newp &&
2493 : 0 : MLX5_TXOFF_CONFIG(MULTI) &&
2494 [ # # # # : 0 : unlikely(NB_SEGS(loc->mbuf) > 1))
# # # # #
# # # # #
# # # # ]
2495 : : return MLX5_TXCMP_CODE_MULTI;
2496 : : /* Check for TSO packet. */
2497 : : if (newp &&
2498 : 0 : MLX5_TXOFF_CONFIG(TSO) &&
2499 [ # # # # : 0 : unlikely(loc->mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG))
# # # # #
# # # # #
# # # # #
# # # #
# ]
2500 : : return MLX5_TXCMP_CODE_TSO;
2501 : : /* Check if eMPW is enabled at all. */
2502 : : if (!MLX5_TXOFF_CONFIG(EMPW))
2503 : : return MLX5_TXCMP_CODE_SINGLE;
2504 : : /* Check if eMPW can be engaged. */
2505 : 0 : if (MLX5_TXOFF_CONFIG(VLAN) &&
2506 [ # # # # : 0 : unlikely(loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) &&
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
2507 : 0 : (!MLX5_TXOFF_CONFIG(INLINE) ||
2508 [ # # # # : 0 : unlikely((rte_pktmbuf_data_len(loc->mbuf) +
# # # # #
# # # # #
# # # # #
# # # #
# ]
2509 : : sizeof(struct rte_vlan_hdr)) > txq->inlen_empw))) {
2510 : : /*
2511 : : * eMPW does not support VLAN insertion offload, we have to
2512 : : * inline the entire packet but packet is too long for inlining.
2513 : : */
2514 : 0 : return MLX5_TXCMP_CODE_SINGLE;
2515 : : }
2516 : : return MLX5_TXCMP_CODE_EMPW;
2517 : : }
2518 : :
2519 : : /**
2520 : : * Check the next packet attributes to match with the eMPW batch ones.
2521 : : * In addition, for legacy MPW the packet length is checked either.
2522 : : *
2523 : : * @param txq
2524 : : * Pointer to TX queue structure.
2525 : : * @param es
2526 : : * Pointer to Ethernet Segment of eMPW batch.
2527 : : * @param loc
2528 : : * Pointer to burst routine local context.
2529 : : * @param dlen
2530 : : * Length of previous packet in MPW descriptor.
2531 : : * @param olx
2532 : : * Configured Tx offloads mask. It is fully defined at
2533 : : * compile time and may be used for optimization.
2534 : : *
2535 : : * @return
2536 : : * true - packet match with eMPW batch attributes.
2537 : : * false - no match, eMPW should be restarted.
2538 : : */
2539 : : static __rte_always_inline bool
2540 : : mlx5_tx_match_empw(struct mlx5_txq_data *__rte_restrict txq,
2541 : : struct mlx5_wqe_eseg *__rte_restrict es,
2542 : : struct mlx5_txq_local *__rte_restrict loc,
2543 : : uint32_t dlen,
2544 : : unsigned int olx)
2545 : : {
2546 : : uint8_t swp_flags = 0;
2547 : :
2548 : : /* Compare the checksum flags, if any. */
2549 : 0 : if (MLX5_TXOFF_CONFIG(CSUM) &&
2550 [ # # # # : 0 : txq_ol_cksum_to_cs(loc->mbuf) != es->cs_flags)
# # # # #
# # # ]
2551 : : return false;
2552 : : /* Compare the Software Parser offsets and flags. */
2553 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(SWP) &&
# # # # #
# # # ]
2554 [ # # # # : 0 : (es->swp_offs != txq_mbuf_to_swp(loc, &swp_flags, olx) ||
# # # # #
# # # ]
2555 [ # # # # : 0 : es->swp_flags != swp_flags))
# # # # #
# # # ]
2556 : : return false;
2557 : : /* Fill metadata field if needed. */
2558 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(METADATA) &&
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
2559 : 0 : es->metadata != (loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
2560 [ # # # # : 0 : rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) : 0))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # ]
2561 : : return false;
2562 : : /* Legacy MPW can send packets with the same length only. */
2563 : 0 : if (MLX5_TXOFF_CONFIG(MPW) &&
2564 [ # # # # : 0 : dlen != rte_pktmbuf_data_len(loc->mbuf))
# # # # ]
2565 : : return false;
2566 : : /* There must be no VLAN packets in eMPW loop. */
2567 : : if (MLX5_TXOFF_CONFIG(VLAN))
2568 : : MLX5_ASSERT(!(loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN));
2569 : : /* Check if the scheduling is requested. */
2570 : 0 : if (MLX5_TXOFF_CONFIG(TXPP) &&
2571 [ # # # # : 0 : loc->mbuf->ol_flags & txq->ts_mask)
# # # # #
# # # ]
2572 : : return false;
2573 : : return true;
2574 : : }
2575 : :
2576 : : /**
2577 : : * Update send loop variables and WQE for eMPW loop without data inlining.
2578 : : * Number of Data Segments is equal to the number of sent packets.
2579 : : *
2580 : : * @param txq
2581 : : * Pointer to TX queue structure.
2582 : : * @param loc
2583 : : * Pointer to burst routine local context.
2584 : : * @param ds
2585 : : * Number of packets/Data Segments/Packets.
2586 : : * @param slen
2587 : : * Accumulated statistics, bytes sent.
2588 : : * @param olx
2589 : : * Configured Tx offloads mask. It is fully defined at
2590 : : * compile time and may be used for optimization.
2591 : : *
2592 : : * @return
2593 : : * true - packet match with eMPW batch attributes.
2594 : : * false - no match, eMPW should be restarted.
2595 : : */
2596 : : static __rte_always_inline void
2597 : : mlx5_tx_sdone_empw(struct mlx5_txq_data *__rte_restrict txq,
2598 : : struct mlx5_txq_local *__rte_restrict loc,
2599 : : unsigned int ds,
2600 : : unsigned int slen,
2601 : : unsigned int olx __rte_unused)
2602 : : {
2603 : : MLX5_ASSERT(!MLX5_TXOFF_CONFIG(INLINE));
2604 : : #ifdef MLX5_PMD_SOFT_COUNTERS
2605 : : /* Update sent data bytes counter. */
2606 : 0 : txq->stats.obytes += slen;
2607 : : #else
2608 : : (void)slen;
2609 : : #endif
2610 : 0 : loc->elts_free -= ds;
2611 : 0 : loc->pkts_sent += ds;
2612 : 0 : ds += 2;
2613 : 0 : loc->wqe_last->cseg.sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | ds);
2614 : 0 : txq->wqe_ci += (ds + 3) / 4;
2615 : 0 : loc->wqe_free -= (ds + 3) / 4;
2616 : : }
2617 : :
2618 : : /**
2619 : : * Update send loop variables and WQE for eMPW loop with data inlining.
2620 : : * Gets the size of pushed descriptors and data to the WQE.
2621 : : *
2622 : : * @param txq
2623 : : * Pointer to TX queue structure.
2624 : : * @param loc
2625 : : * Pointer to burst routine local context.
2626 : : * @param len
2627 : : * Total size of descriptor/data in bytes.
2628 : : * @param slen
2629 : : * Accumulated statistics, data bytes sent.
2630 : : * @param wqem
2631 : : * The base WQE for the eMPW/MPW descriptor.
2632 : : * @param olx
2633 : : * Configured Tx offloads mask. It is fully defined at
2634 : : * compile time and may be used for optimization.
2635 : : *
2636 : : * @return
2637 : : * true - packet match with eMPW batch attributes.
2638 : : * false - no match, eMPW should be restarted.
2639 : : */
2640 : : static __rte_always_inline void
2641 : : mlx5_tx_idone_empw(struct mlx5_txq_data *__rte_restrict txq,
2642 : : struct mlx5_txq_local *__rte_restrict loc,
2643 : : unsigned int len,
2644 : : unsigned int slen,
2645 : : struct mlx5_wqe *__rte_restrict wqem,
2646 : : unsigned int olx __rte_unused)
2647 : : {
2648 : : struct mlx5_wqe_dseg *dseg = &wqem->dseg[0];
2649 : :
2650 : : MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
2651 : : #ifdef MLX5_PMD_SOFT_COUNTERS
2652 : : /* Update sent data bytes counter. */
2653 : 0 : txq->stats.obytes += slen;
2654 : : #else
2655 : : (void)slen;
2656 : : #endif
2657 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(MPW) && dseg->bcount == RTE_BE32(0)) {
# # # # ]
2658 : : /*
2659 : : * If the legacy MPW session contains the inline packets
2660 : : * we should set the only inline data segment length
2661 : : * and align the total length to the segment size.
2662 : : */
2663 : : MLX5_ASSERT(len > sizeof(dseg->bcount));
2664 [ # # # # : 0 : dseg->bcount = rte_cpu_to_be_32((len - sizeof(dseg->bcount)) |
# # # # #
# # # #
# ]
2665 : : MLX5_ETH_WQE_DATA_INLINE);
2666 : 0 : len = (len + MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE + 2;
2667 : : } else {
2668 : : /*
2669 : : * The session is not legacy MPW or contains the
2670 : : * data buffer pointer segments.
2671 : : */
2672 : : MLX5_ASSERT((len % MLX5_WSEG_SIZE) == 0);
2673 : 0 : len = len / MLX5_WSEG_SIZE + 2;
2674 : : }
2675 [ # # # # : 0 : wqem->cseg.sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | len);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
2676 : 0 : txq->wqe_ci += (len + 3) / 4;
2677 : 0 : loc->wqe_free -= (len + 3) / 4;
2678 : : loc->wqe_last = wqem;
2679 : : }
2680 : :
2681 : : /**
2682 : : * The set of Tx burst functions for single-segment packets without TSO
2683 : : * and with Multi-Packet Writing feature support.
2684 : : * Supports all types of Tx offloads, except multi-packets and TSO.
2685 : : *
2686 : : * Uses MLX5_OPCODE_EMPW to build WQEs if possible and sends as many packet
2687 : : * per WQE as it can. If eMPW is not configured or packet can not be sent with
2688 : : * eMPW (VLAN insertion) the ordinary SEND opcode is used and only one packet
2689 : : * placed in WQE.
2690 : : *
2691 : : * Functions stop sending if it encounters the multi-segment packet or packet
2692 : : * with TSO requested.
2693 : : *
2694 : : * The routines are responsible for storing processed mbuf into elts ring buffer
2695 : : * and update elts_head if inlining offload is requested. Otherwise the copying
2696 : : * mbufs to elts can be postponed and completed at the end of burst routine.
2697 : : *
2698 : : * @param txq
2699 : : * Pointer to TX queue structure.
2700 : : * @param[in] pkts
2701 : : * Packets to transmit.
2702 : : * @param pkts_n
2703 : : * Number of packets in array.
2704 : : * @param loc
2705 : : * Pointer to burst routine local context.
2706 : : * @param olx
2707 : : * Configured Tx offloads mask. It is fully defined at
2708 : : * compile time and may be used for optimization.
2709 : : *
2710 : : * @return
2711 : : * MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
2712 : : * MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
2713 : : * MLX5_TXCMP_CODE_MULTI - multi-segment packet encountered.
2714 : : * MLX5_TXCMP_CODE_TSO - TSO packet encountered.
2715 : : * MLX5_TXCMP_CODE_SINGLE - used inside functions set.
2716 : : * MLX5_TXCMP_CODE_EMPW - used inside functions set.
2717 : : *
2718 : : * Local context variables updated.
2719 : : *
2720 : : *
2721 : : * The routine sends packets with MLX5_OPCODE_EMPW
2722 : : * without inlining, this is dedicated optimized branch.
2723 : : * No VLAN insertion is supported.
2724 : : */
2725 : : static __rte_always_inline enum mlx5_txcmp_code
2726 : : mlx5_tx_burst_empw_simple(struct mlx5_txq_data *__rte_restrict txq,
2727 : : struct rte_mbuf **__rte_restrict pkts,
2728 : : unsigned int pkts_n,
2729 : : struct mlx5_txq_local *__rte_restrict loc,
2730 : : unsigned int olx)
2731 : : {
2732 : : /*
2733 : : * Subroutine is the part of mlx5_tx_burst_single() and sends
2734 : : * single-segment packet with eMPW opcode without data inlining.
2735 : : */
2736 : : MLX5_ASSERT(!MLX5_TXOFF_CONFIG(INLINE));
2737 : : MLX5_ASSERT(MLX5_TXOFF_CONFIG(EMPW));
2738 : : MLX5_ASSERT(loc->elts_free && loc->wqe_free);
2739 : : MLX5_ASSERT(pkts_n > loc->pkts_sent);
2740 : 0 : pkts += loc->pkts_sent + 1;
2741 : 0 : pkts_n -= loc->pkts_sent;
2742 : : for (;;) {
2743 : : struct mlx5_wqe_dseg *__rte_restrict dseg;
2744 : : struct mlx5_wqe_eseg *__rte_restrict eseg;
2745 : : enum mlx5_txcmp_code ret;
2746 : : unsigned int part, loop;
2747 : : unsigned int slen = 0;
2748 : :
2749 : 0 : next_empw:
2750 : : MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
2751 : 0 : part = RTE_MIN(pkts_n, MLX5_TXOFF_CONFIG(MPW) ?
2752 : : MLX5_MPW_MAX_PACKETS :
2753 : : MLX5_EMPW_MAX_PACKETS);
2754 [ # # # # : 0 : if (unlikely(loc->elts_free < part)) {
# # # # #
# # # # #
# # ]
2755 : : /* We have no enough elts to save all mbufs. */
2756 [ # # # # : 0 : if (unlikely(loc->elts_free < MLX5_EMPW_MIN_PACKETS))
# # # # #
# # # # #
# # ]
2757 : : return MLX5_TXCMP_CODE_EXIT;
2758 : : /* But we still able to send at least minimal eMPW. */
2759 : : part = loc->elts_free;
2760 : : }
2761 : : if (MLX5_TXOFF_CONFIG(TXPP)) {
2762 : : enum mlx5_txcmp_code wret;
2763 : :
2764 : : /* Generate WAIT for scheduling if requested. */
2765 : : wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
2766 : : if (wret == MLX5_TXCMP_CODE_EXIT)
2767 : : return MLX5_TXCMP_CODE_EXIT;
2768 : : if (wret == MLX5_TXCMP_CODE_ERROR)
2769 : : return MLX5_TXCMP_CODE_ERROR;
2770 : : }
2771 : : /* Check whether we have enough WQEs */
2772 [ # # # # : 0 : if (unlikely(loc->wqe_free < ((2 + part + 3) / 4))) {
# # # # #
# # # # #
# # ]
2773 [ # # # # : 0 : if (unlikely(loc->wqe_free <
# # # # #
# # # # #
# # ]
2774 : : ((2 + MLX5_EMPW_MIN_PACKETS + 3) / 4)))
2775 : : return MLX5_TXCMP_CODE_EXIT;
2776 : 0 : part = (loc->wqe_free * 4) - 2;
2777 : : }
2778 [ # # # # : 0 : if (likely(part > 1))
# # # # #
# # # # #
# # ]
2779 : 0 : rte_prefetch0(*pkts);
2780 : 0 : loc->wqe_last = txq->wqes + (txq->wqe_ci & txq->wqe_m);
2781 : : /*
2782 : : * Build eMPW title WQEBB:
2783 : : * - Control Segment, eMPW opcode
2784 : : * - Ethernet Segment, no inline
2785 : : */
2786 [ # # # # : 0 : mlx5_tx_cseg_init(txq, loc, loc->wqe_last, part + 2,
# # # # #
# # # # #
# # ]
2787 : : MLX5_OPCODE_ENHANCED_MPSW, olx);
2788 : : mlx5_tx_eseg_none(txq, loc, loc->wqe_last,
2789 : : olx & ~MLX5_TXOFF_CONFIG_VLAN);
2790 : : eseg = &loc->wqe_last->eseg;
2791 : 0 : dseg = &loc->wqe_last->dseg[0];
2792 : : loop = part;
2793 : : /* Store the packet length for legacy MPW. */
2794 : : if (MLX5_TXOFF_CONFIG(MPW))
2795 [ # # # # ]: 0 : eseg->mss = rte_cpu_to_be_16
2796 : : (rte_pktmbuf_data_len(loc->mbuf));
2797 : : for (;;) {
2798 : 0 : uint32_t dlen = rte_pktmbuf_data_len(loc->mbuf);
2799 : : #ifdef MLX5_PMD_SOFT_COUNTERS
2800 : : /* Update sent data bytes counter. */
2801 : 0 : slen += dlen;
2802 : : #endif
2803 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
2804 : : mlx5_tx_dseg_ptr
2805 : : (txq, loc, dseg,
2806 [ # # # # : 0 : rte_pktmbuf_mtod(loc->mbuf, uint8_t *),
# # # # #
# # # # #
# # ]
2807 : : dlen, olx);
2808 [ # # # # : 0 : if (unlikely(--loop == 0))
# # # # #
# # # # #
# # ]
2809 : : break;
2810 : 0 : loc->mbuf = *pkts++;
2811 [ # # # # : 0 : if (likely(loop > 1))
# # # # #
# # # # #
# # ]
2812 : 0 : rte_prefetch0(*pkts);
2813 : : ret = mlx5_tx_able_to_empw(txq, loc, olx, true);
2814 : : /*
2815 : : * Unroll the completion code to avoid
2816 : : * returning variable value - it results in
2817 : : * unoptimized sequent checking in caller.
2818 : : */
2819 : : if (ret == MLX5_TXCMP_CODE_MULTI) {
2820 [ # # # # : 0 : part -= loop;
# # ]
2821 : : mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2822 [ # # # # : 0 : if (unlikely(!loc->elts_free ||
# # # # #
# # # ]
2823 : : !loc->wqe_free))
2824 : : return MLX5_TXCMP_CODE_EXIT;
2825 : : return MLX5_TXCMP_CODE_MULTI;
2826 : : }
2827 : : MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
2828 : : if (ret == MLX5_TXCMP_CODE_TSO) {
2829 [ # # # # : 0 : part -= loop;
# # ]
2830 : : mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2831 [ # # # # : 0 : if (unlikely(!loc->elts_free ||
# # # # #
# # # ]
2832 : : !loc->wqe_free))
2833 : : return MLX5_TXCMP_CODE_EXIT;
2834 : : return MLX5_TXCMP_CODE_TSO;
2835 : : }
2836 : : if (ret == MLX5_TXCMP_CODE_SINGLE) {
2837 [ # # # # : 0 : part -= loop;
# # ]
2838 : : mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2839 [ # # # # : 0 : if (unlikely(!loc->elts_free ||
# # # # #
# # # ]
2840 : : !loc->wqe_free))
2841 : : return MLX5_TXCMP_CODE_EXIT;
2842 : : return MLX5_TXCMP_CODE_SINGLE;
2843 : : }
2844 : : if (ret != MLX5_TXCMP_CODE_EMPW) {
2845 : : MLX5_ASSERT(false);
2846 : : part -= loop;
2847 : : mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2848 : : return MLX5_TXCMP_CODE_ERROR;
2849 : : }
2850 : : /*
2851 : : * Check whether packet parameters coincide
2852 : : * within assumed eMPW batch:
2853 : : * - check sum settings
2854 : : * - metadata value
2855 : : * - software parser settings
2856 : : * - packets length (legacy MPW only)
2857 : : * - scheduling is not required
2858 : : */
2859 : : if (!mlx5_tx_match_empw(txq, eseg, loc, dlen, olx)) {
2860 : : MLX5_ASSERT(loop);
2861 [ # # # # : 0 : part -= loop;
# # # # #
# # # #
# ]
2862 : : mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2863 [ # # # # : 0 : if (unlikely(!loc->elts_free ||
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
2864 : : !loc->wqe_free))
2865 : : return MLX5_TXCMP_CODE_EXIT;
2866 : 0 : pkts_n -= part;
2867 : 0 : goto next_empw;
2868 : : }
2869 : : /* Packet attributes match, continue the same eMPW. */
2870 : 0 : ++dseg;
2871 [ # # # # : 0 : if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
# # # # #
# # # # #
# # ]
2872 : 0 : dseg = (struct mlx5_wqe_dseg *)txq->wqes;
2873 : : }
2874 : : /* eMPW is built successfully, update loop parameters. */
2875 : : MLX5_ASSERT(!loop);
2876 : : MLX5_ASSERT(pkts_n >= part);
2877 : : #ifdef MLX5_PMD_SOFT_COUNTERS
2878 : : /* Update sent data bytes counter. */
2879 : 0 : txq->stats.obytes += slen;
2880 : : #endif
2881 : 0 : loc->elts_free -= part;
2882 : 0 : loc->pkts_sent += part;
2883 : 0 : txq->wqe_ci += (2 + part + 3) / 4;
2884 : 0 : loc->wqe_free -= (2 + part + 3) / 4;
2885 : 0 : pkts_n -= part;
2886 [ # # # # : 0 : if (unlikely(!pkts_n || !loc->elts_free || !loc->wqe_free))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
2887 : : return MLX5_TXCMP_CODE_EXIT;
2888 [ # # # # : 0 : loc->mbuf = *pkts++;
# # # # #
# ]
2889 : : ret = mlx5_tx_able_to_empw(txq, loc, olx, true);
2890 [ # # # # : 0 : if (unlikely(ret != MLX5_TXCMP_CODE_EMPW))
# # # # #
# ]
2891 : : return ret;
2892 : : /* Continue sending eMPW batches. */
2893 : : }
2894 : : MLX5_ASSERT(false);
2895 : : }
2896 : :
2897 : : /**
2898 : : * The routine sends packets with MLX5_OPCODE_EMPW
2899 : : * with inlining, optionally supports VLAN insertion.
2900 : : */
2901 : : static __rte_always_inline enum mlx5_txcmp_code
2902 : : mlx5_tx_burst_empw_inline(struct mlx5_txq_data *__rte_restrict txq,
2903 : : struct rte_mbuf **__rte_restrict pkts,
2904 : : unsigned int pkts_n,
2905 : : struct mlx5_txq_local *__rte_restrict loc,
2906 : : unsigned int olx)
2907 : : {
2908 : : /*
2909 : : * Subroutine is the part of mlx5_tx_burst_single() and sends
2910 : : * single-segment packet with eMPW opcode with data inlining.
2911 : : */
2912 : : MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
2913 : : MLX5_ASSERT(MLX5_TXOFF_CONFIG(EMPW));
2914 : : MLX5_ASSERT(loc->elts_free && loc->wqe_free);
2915 : : MLX5_ASSERT(pkts_n > loc->pkts_sent);
2916 : 0 : pkts += loc->pkts_sent + 1;
2917 : 0 : pkts_n -= loc->pkts_sent;
2918 : : for (;;) {
2919 : : struct mlx5_wqe_dseg *__rte_restrict dseg;
2920 : : struct mlx5_wqe *__rte_restrict wqem;
2921 : : enum mlx5_txcmp_code ret;
2922 : : unsigned int room, part, nlim;
2923 : : unsigned int slen = 0;
2924 : :
2925 : : MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
2926 : : /*
2927 : : * Limits the amount of packets in one WQE
2928 : : * to improve CQE latency generation.
2929 : : */
2930 : 0 : nlim = RTE_MIN(pkts_n, MLX5_TXOFF_CONFIG(MPW) ?
2931 : : MLX5_MPW_INLINE_MAX_PACKETS :
2932 : : MLX5_EMPW_MAX_PACKETS);
2933 : : if (MLX5_TXOFF_CONFIG(TXPP)) {
2934 : : enum mlx5_txcmp_code wret;
2935 : :
2936 : : /* Generate WAIT for scheduling if requested. */
2937 [ # # # # : 0 : wret = mlx5_tx_schedule_send(txq, loc, nlim, olx);
# # # # ]
2938 : : if (wret == MLX5_TXCMP_CODE_EXIT)
2939 : : return MLX5_TXCMP_CODE_EXIT;
2940 : : if (wret == MLX5_TXCMP_CODE_ERROR)
2941 : : return MLX5_TXCMP_CODE_ERROR;
2942 : : }
2943 : : /* Check whether we have minimal amount WQEs */
2944 [ # # # # : 0 : if (unlikely(loc->wqe_free <
# # # # #
# # # #
# ]
2945 : : ((2 + MLX5_EMPW_MIN_PACKETS + 3) / 4)))
2946 : : return MLX5_TXCMP_CODE_EXIT;
2947 [ # # # # : 0 : if (likely(pkts_n > 1))
# # # # #
# # # #
# ]
2948 : 0 : rte_prefetch0(*pkts);
2949 [ # # # # : 0 : wqem = txq->wqes + (txq->wqe_ci & txq->wqe_m);
# # # # #
# # # #
# ]
2950 : : /*
2951 : : * Build eMPW title WQEBB:
2952 : : * - Control Segment, eMPW opcode, zero DS
2953 : : * - Ethernet Segment, no inline
2954 : : */
2955 : : mlx5_tx_cseg_init(txq, loc, wqem, 0,
2956 : : MLX5_OPCODE_ENHANCED_MPSW, olx);
2957 : : mlx5_tx_eseg_none(txq, loc, wqem,
2958 : : olx & ~MLX5_TXOFF_CONFIG_VLAN);
2959 : 0 : dseg = &wqem->dseg[0];
2960 : : /* Store the packet length for legacy MPW. */
2961 : : if (MLX5_TXOFF_CONFIG(MPW))
2962 [ # # # # ]: 0 : wqem->eseg.mss = rte_cpu_to_be_16
2963 : : (rte_pktmbuf_data_len(loc->mbuf));
2964 : 0 : room = RTE_MIN(MLX5_WQE_SIZE_MAX / MLX5_WQE_SIZE,
2965 : : loc->wqe_free) * MLX5_WQE_SIZE -
2966 : 0 : MLX5_WQE_CSEG_SIZE -
2967 : : MLX5_WQE_ESEG_SIZE;
2968 : : /* Limit the room for legacy MPW sessions for performance. */
2969 : : if (MLX5_TXOFF_CONFIG(MPW))
2970 : 0 : room = RTE_MIN(room,
2971 : : RTE_MAX(txq->inlen_empw +
2972 : : sizeof(dseg->bcount) +
2973 : : (MLX5_TXOFF_CONFIG(VLAN) ?
2974 : : sizeof(struct rte_vlan_hdr) : 0),
2975 : : MLX5_MPW_INLINE_MAX_PACKETS *
2976 : : MLX5_WQE_DSEG_SIZE));
2977 : : /* Build WQE till we have space, packets and resources. */
2978 : : part = room;
2979 : : for (;;) {
2980 : 0 : uint32_t dlen = rte_pktmbuf_data_len(loc->mbuf);
2981 : 0 : uint8_t *dptr = rte_pktmbuf_mtod(loc->mbuf, uint8_t *);
2982 : : unsigned int tlen;
2983 : :
2984 : : MLX5_ASSERT(room >= MLX5_WQE_DSEG_SIZE);
2985 : : MLX5_ASSERT((room % MLX5_WQE_DSEG_SIZE) == 0);
2986 : : MLX5_ASSERT((uintptr_t)dseg < (uintptr_t)txq->wqes_end);
2987 : : /*
2988 : : * Some Tx offloads may cause an error if packet is not
2989 : : * long enough, check against assumed minimal length.
2990 : : */
2991 [ # # # # : 0 : if (unlikely(dlen <= MLX5_ESEG_MIN_INLINE_SIZE)) {
# # # # #
# # # #
# ]
2992 : 0 : part -= room;
2993 [ # # # # : 0 : if (unlikely(!part))
# # # # #
# # # #
# ]
2994 : : return MLX5_TXCMP_CODE_ERROR;
2995 : : /*
2996 : : * We have some successfully built
2997 : : * packet Data Segments to send.
2998 : : */
2999 : : mlx5_tx_idone_empw(txq, loc, part,
3000 : : slen, wqem, olx);
3001 : : return MLX5_TXCMP_CODE_ERROR;
3002 : : }
3003 : : /* Inline or not inline - that's the Question. */
3004 [ # # # # : 0 : if (dlen > txq->inlen_empw ||
# # # # #
# # # #
# ]
3005 [ # # # # : 0 : loc->mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE)
# # # # #
# # # #
# ]
3006 [ # # # # : 0 : goto pointer_empw;
# # # # #
# # # #
# ]
3007 : : if (MLX5_TXOFF_CONFIG(MPW)) {
3008 [ # # # # ]: 0 : if (dlen > txq->inlen_send)
3009 : 0 : goto pointer_empw;
3010 : : tlen = dlen;
3011 [ # # # # ]: 0 : if (part == room) {
3012 : : /* Open new inline MPW session. */
3013 : 0 : tlen += sizeof(dseg->bcount);
3014 : 0 : dseg->bcount = RTE_BE32(0);
3015 : 0 : dseg = RTE_PTR_ADD
3016 : : (dseg, sizeof(dseg->bcount));
3017 : : } else {
3018 : : /*
3019 : : * No pointer and inline descriptor
3020 : : * intermix for legacy MPW sessions.
3021 : : */
3022 [ # # # # ]: 0 : if (wqem->dseg[0].bcount)
3023 : : break;
3024 : : }
3025 : : } else {
3026 : 0 : tlen = sizeof(dseg->bcount) + dlen;
3027 : : }
3028 : : /* Inline entire packet, optional VLAN insertion. */
3029 : 0 : if (MLX5_TXOFF_CONFIG(VLAN) &&
3030 [ # # # # : 0 : loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) {
# # # # ]
3031 : : /*
3032 : : * The packet length must be checked in
3033 : : * mlx5_tx_able_to_empw() and packet
3034 : : * fits into inline length guaranteed.
3035 : : */
3036 : : MLX5_ASSERT((dlen +
3037 : : sizeof(struct rte_vlan_hdr)) <=
3038 : : txq->inlen_empw);
3039 : 0 : tlen += sizeof(struct rte_vlan_hdr);
3040 [ # # # # : 0 : if (room < tlen)
# # # # ]
3041 : : break;
3042 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3043 : : dseg = mlx5_tx_dseg_vlan(txq, loc, dseg,
3044 : : dptr, dlen, olx);
3045 : : #ifdef MLX5_PMD_SOFT_COUNTERS
3046 : : /* Update sent data bytes counter. */
3047 : 0 : slen += sizeof(struct rte_vlan_hdr);
3048 : : #endif
3049 : : } else {
3050 [ # # # # : 0 : if (room < tlen)
# # # # #
# # # #
# ]
3051 : : break;
3052 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3053 : : dseg = mlx5_tx_dseg_empw(txq, loc, dseg,
3054 : : dptr, dlen, olx);
3055 : : }
3056 : : if (!MLX5_TXOFF_CONFIG(MPW))
3057 : 0 : tlen = RTE_ALIGN(tlen, MLX5_WSEG_SIZE);
3058 : : MLX5_ASSERT(room >= tlen);
3059 : 0 : room -= tlen;
3060 : : /*
3061 : : * Packet data are completely inline,
3062 : : * we can try to free the packet.
3063 : : */
3064 [ # # # # : 0 : if (likely(loc->pkts_sent == loc->mbuf_free)) {
# # # # #
# # # #
# ]
3065 : : /*
3066 : : * All the packets from the burst beginning
3067 : : * are inline, we can free mbufs directly
3068 : : * from the origin array on tx_burst exit().
3069 : : */
3070 : 0 : loc->mbuf_free++;
3071 : 0 : goto next_mbuf;
3072 : : }
3073 : : /*
3074 : : * In order no to call rte_pktmbuf_free_seg() here,
3075 : : * in the most inner loop (that might be very
3076 : : * expensive) we just save the mbuf in elts.
3077 : : */
3078 : 0 : txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
3079 : 0 : loc->elts_free--;
3080 : 0 : goto next_mbuf;
3081 : 0 : pointer_empw:
3082 : : /*
3083 : : * No pointer and inline descriptor
3084 : : * intermix for legacy MPW sessions.
3085 : : */
3086 [ # # # # ]: 0 : if (MLX5_TXOFF_CONFIG(MPW) &&
3087 : 0 : part != room &&
3088 [ # # # # ]: 0 : wqem->dseg[0].bcount == RTE_BE32(0))
3089 : : break;
3090 : : /*
3091 : : * Not inlinable VLAN packets are
3092 : : * proceeded outside of this routine.
3093 : : */
3094 : : MLX5_ASSERT(room >= MLX5_WQE_DSEG_SIZE);
3095 : : if (MLX5_TXOFF_CONFIG(VLAN))
3096 : : MLX5_ASSERT(!(loc->mbuf->ol_flags &
3097 : : RTE_MBUF_F_TX_VLAN));
3098 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3099 : : mlx5_tx_dseg_ptr(txq, loc, dseg, dptr, dlen, olx);
3100 : : /* We have to store mbuf in elts.*/
3101 : 0 : txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
3102 : 0 : loc->elts_free--;
3103 : 0 : room -= MLX5_WQE_DSEG_SIZE;
3104 : : /* Ring buffer wraparound is checked at the loop end.*/
3105 : 0 : ++dseg;
3106 : 0 : next_mbuf:
3107 : : #ifdef MLX5_PMD_SOFT_COUNTERS
3108 : : /* Update sent data bytes counter. */
3109 : 0 : slen += dlen;
3110 : : #endif
3111 : 0 : loc->pkts_sent++;
3112 : 0 : pkts_n--;
3113 [ # # # # : 0 : if (unlikely(!pkts_n || !loc->elts_free)) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
3114 : : /*
3115 : : * We have no resources/packets to
3116 : : * continue build descriptors.
3117 : : */
3118 [ # # # # : 0 : part -= room;
# # # # #
# # # #
# ]
3119 : : mlx5_tx_idone_empw(txq, loc, part,
3120 : : slen, wqem, olx);
3121 : : return MLX5_TXCMP_CODE_EXIT;
3122 : : }
3123 : 0 : loc->mbuf = *pkts++;
3124 [ # # # # : 0 : if (likely(pkts_n > 1))
# # # # #
# # # #
# ]
3125 : 0 : rte_prefetch0(*pkts);
3126 : : ret = mlx5_tx_able_to_empw(txq, loc, olx, true);
3127 : : /*
3128 : : * Unroll the completion code to avoid
3129 : : * returning variable value - it results in
3130 : : * unoptimized sequent checking in caller.
3131 : : */
3132 : : if (ret == MLX5_TXCMP_CODE_MULTI) {
3133 [ # # # # : 0 : part -= room;
# # ]
3134 : : mlx5_tx_idone_empw(txq, loc, part,
3135 : : slen, wqem, olx);
3136 [ # # # # : 0 : if (unlikely(!loc->elts_free ||
# # ]
3137 : : !loc->wqe_free))
3138 : : return MLX5_TXCMP_CODE_EXIT;
3139 : : return MLX5_TXCMP_CODE_MULTI;
3140 : : }
3141 : : MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
3142 : : if (ret == MLX5_TXCMP_CODE_TSO) {
3143 [ # # # # : 0 : part -= room;
# # ]
3144 : : mlx5_tx_idone_empw(txq, loc, part,
3145 : : slen, wqem, olx);
3146 [ # # # # : 0 : if (unlikely(!loc->elts_free ||
# # ]
3147 : : !loc->wqe_free))
3148 : : return MLX5_TXCMP_CODE_EXIT;
3149 : : return MLX5_TXCMP_CODE_TSO;
3150 : : }
3151 : : if (ret == MLX5_TXCMP_CODE_SINGLE) {
3152 [ # # # # : 0 : part -= room;
# # # # ]
3153 : : mlx5_tx_idone_empw(txq, loc, part,
3154 : : slen, wqem, olx);
3155 [ # # # # : 0 : if (unlikely(!loc->elts_free ||
# # # # ]
3156 : : !loc->wqe_free))
3157 : : return MLX5_TXCMP_CODE_EXIT;
3158 : : return MLX5_TXCMP_CODE_SINGLE;
3159 : : }
3160 : : if (ret != MLX5_TXCMP_CODE_EMPW) {
3161 : : MLX5_ASSERT(false);
3162 : : part -= room;
3163 : : mlx5_tx_idone_empw(txq, loc, part,
3164 : : slen, wqem, olx);
3165 : : return MLX5_TXCMP_CODE_ERROR;
3166 : : }
3167 : : /* Check if we have minimal room left. */
3168 : 0 : nlim--;
3169 [ # # # # : 0 : if (unlikely(!nlim || room < MLX5_WQE_DSEG_SIZE))
# # # # #
# # # #
# ]
3170 : : break;
3171 : : /*
3172 : : * Check whether packet parameters coincide
3173 : : * within assumed eMPW batch:
3174 : : * - check sum settings
3175 : : * - metadata value
3176 : : * - software parser settings
3177 : : * - packets length (legacy MPW only)
3178 : : * - scheduling is not required
3179 : : */
3180 : : if (!mlx5_tx_match_empw(txq, &wqem->eseg,
3181 : : loc, dlen, olx))
3182 : : break;
3183 : : /* Packet attributes match, continue the same eMPW. */
3184 [ # # # # : 0 : if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
# # # # #
# # # #
# ]
3185 : 0 : dseg = (struct mlx5_wqe_dseg *)txq->wqes;
3186 : : }
3187 : : /*
3188 : : * We get here to close an existing eMPW
3189 : : * session and start the new one.
3190 : : */
3191 : : MLX5_ASSERT(pkts_n);
3192 : 0 : part -= room;
3193 [ # # # # : 0 : if (unlikely(!part))
# # # # #
# # # #
# ]
3194 : : return MLX5_TXCMP_CODE_EXIT;
3195 : : mlx5_tx_idone_empw(txq, loc, part, slen, wqem, olx);
3196 [ # # # # : 0 : if (unlikely(!loc->elts_free ||
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
3197 : : !loc->wqe_free))
3198 : : return MLX5_TXCMP_CODE_EXIT;
3199 : : /* Continue the loop with new eMPW session. */
3200 : : }
3201 : : MLX5_ASSERT(false);
3202 : : }
3203 : :
3204 : : /**
3205 : : * The routine sends packets with ordinary MLX5_OPCODE_SEND.
3206 : : * Data inlining and VLAN insertion are supported.
3207 : : */
3208 : : static __rte_always_inline enum mlx5_txcmp_code
3209 : : mlx5_tx_burst_single_send(struct mlx5_txq_data *__rte_restrict txq,
3210 : : struct rte_mbuf **__rte_restrict pkts,
3211 : : unsigned int pkts_n,
3212 : : struct mlx5_txq_local *__rte_restrict loc,
3213 : : unsigned int olx)
3214 : : {
3215 : : /*
3216 : : * Subroutine is the part of mlx5_tx_burst_single()
3217 : : * and sends single-segment packet with SEND opcode.
3218 : : */
3219 : : MLX5_ASSERT(loc->elts_free && loc->wqe_free);
3220 : : MLX5_ASSERT(pkts_n > loc->pkts_sent);
3221 : 0 : pkts += loc->pkts_sent + 1;
3222 : 0 : pkts_n -= loc->pkts_sent;
3223 : : for (;;) {
3224 : : struct mlx5_wqe *__rte_restrict wqe;
3225 : : enum mlx5_txcmp_code ret;
3226 : :
3227 : : MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
3228 : : MLX5_ASSERT(loc->elts_free);
3229 : : if (MLX5_TXOFF_CONFIG(TXPP)) {
3230 : : enum mlx5_txcmp_code wret;
3231 : :
3232 : : /* Generate WAIT for scheduling if requested. */
3233 : : wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
3234 : : if (wret == MLX5_TXCMP_CODE_EXIT)
3235 : : return MLX5_TXCMP_CODE_EXIT;
3236 : : if (wret == MLX5_TXCMP_CODE_ERROR)
3237 : : return MLX5_TXCMP_CODE_ERROR;
3238 : : }
3239 : : if (MLX5_TXOFF_CONFIG(INLINE)) {
3240 : : unsigned int inlen, vlan = 0;
3241 : :
3242 : 0 : inlen = rte_pktmbuf_data_len(loc->mbuf);
3243 : 0 : if (MLX5_TXOFF_CONFIG(VLAN) &&
3244 [ # # # # : 0 : loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) {
# # # # ]
3245 : : vlan = sizeof(struct rte_vlan_hdr);
3246 : 0 : inlen += vlan;
3247 : : }
3248 : : /*
3249 : : * If inlining is enabled at configuration time
3250 : : * the limit must be not less than minimal size.
3251 : : * Otherwise we would do extra check for data
3252 : : * size to avoid crashes due to length overflow.
3253 : : */
3254 : : MLX5_ASSERT(txq->inlen_send >=
3255 : : MLX5_ESEG_MIN_INLINE_SIZE);
3256 [ # # # # : 0 : if (inlen <= txq->inlen_send) {
# # # # #
# # # #
# ]
3257 : : unsigned int seg_n, wqe_n;
3258 : :
3259 : 0 : rte_prefetch0(rte_pktmbuf_mtod
3260 : : (loc->mbuf, uint8_t *));
3261 : : /* Check against minimal length. */
3262 [ # # # # : 0 : if (inlen <= MLX5_ESEG_MIN_INLINE_SIZE)
# # # # #
# # # #
# ]
3263 : : return MLX5_TXCMP_CODE_ERROR;
3264 [ # # # # : 0 : if (loc->mbuf->ol_flags &
# # # # #
# # # #
# ]
3265 : : RTE_MBUF_F_TX_DYNF_NOINLINE) {
3266 : : /*
3267 : : * The hint flag not to inline packet
3268 : : * data is set. Check whether we can
3269 : : * follow the hint.
3270 : : */
3271 : : if ((!MLX5_TXOFF_CONFIG(EMPW) &&
3272 [ # # # # : 0 : txq->inlen_mode) ||
# # # # #
# # # #
# ]
3273 : : (MLX5_TXOFF_CONFIG(MPW) &&
3274 : : txq->inlen_mode)) {
3275 : : if (inlen <= txq->inlen_send)
3276 : 0 : goto single_inline;
3277 : : /*
3278 : : * The hardware requires the
3279 : : * minimal inline data header.
3280 : : */
3281 : : goto single_min_inline;
3282 : : }
3283 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(VLAN) &&
# # # # ]
3284 [ # # # # : 0 : vlan && !txq->vlan_en) {
# # # # ]
3285 : : /*
3286 : : * We must insert VLAN tag
3287 : : * by software means.
3288 : : */
3289 : 0 : goto single_part_inline;
3290 : : }
3291 : 0 : goto single_no_inline;
3292 : : }
3293 : 0 : single_inline:
3294 : : /*
3295 : : * Completely inlined packet data WQE:
3296 : : * - Control Segment, SEND opcode
3297 : : * - Ethernet Segment, no VLAN insertion
3298 : : * - Data inlined, VLAN optionally inserted
3299 : : * - Alignment to MLX5_WSEG_SIZE
3300 : : * Have to estimate amount of WQEBBs
3301 : : */
3302 : 0 : seg_n = (inlen + 3 * MLX5_WSEG_SIZE -
3303 : : MLX5_ESEG_MIN_INLINE_SIZE +
3304 : 0 : MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
3305 : : /* Check if there are enough WQEBBs. */
3306 : 0 : wqe_n = (seg_n + 3) / 4;
3307 [ # # # # : 0 : if (wqe_n > loc->wqe_free)
# # # # #
# # # #
# ]
3308 : : return MLX5_TXCMP_CODE_EXIT;
3309 [ # # # # : 0 : wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
# # # # #
# # # #
# ]
3310 : : loc->wqe_last = wqe;
3311 : : mlx5_tx_cseg_init(txq, loc, wqe, seg_n,
3312 : : MLX5_OPCODE_SEND, olx);
3313 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3314 : : mlx5_tx_eseg_data(txq, loc, wqe,
3315 : : vlan, inlen, 0, olx);
3316 : 0 : txq->wqe_ci += wqe_n;
3317 [ # # # # : 0 : loc->wqe_free -= wqe_n;
# # # # ]
3318 : : /*
3319 : : * Packet data are completely inlined,
3320 : : * free the packet immediately.
3321 : : */
3322 : : rte_pktmbuf_free_seg(loc->mbuf);
3323 : : } else if ((!MLX5_TXOFF_CONFIG(EMPW) ||
3324 : 0 : MLX5_TXOFF_CONFIG(MPW)) &&
3325 [ # # # # : 0 : txq->inlen_mode) {
# # # # #
# # # #
# ]
3326 : : /*
3327 : : * If minimal inlining is requested the eMPW
3328 : : * feature should be disabled due to data is
3329 : : * inlined into Ethernet Segment, which can
3330 : : * not contain inlined data for eMPW due to
3331 : : * segment shared for all packets.
3332 : : */
3333 : : struct mlx5_wqe_dseg *__rte_restrict dseg;
3334 : : unsigned int ds;
3335 : : uint8_t *dptr;
3336 : :
3337 : : /*
3338 : : * The inline-mode settings require
3339 : : * to inline the specified amount of
3340 : : * data bytes to the Ethernet Segment.
3341 : : * We should check the free space in
3342 : : * WQE ring buffer to inline partially.
3343 : : */
3344 : 0 : single_min_inline:
3345 : : MLX5_ASSERT(txq->inlen_send >= txq->inlen_mode);
3346 : : MLX5_ASSERT(inlen > txq->inlen_mode);
3347 : : MLX5_ASSERT(txq->inlen_mode >=
3348 : : MLX5_ESEG_MIN_INLINE_SIZE);
3349 : : /*
3350 : : * Check whether there are enough free WQEBBs:
3351 : : * - Control Segment
3352 : : * - Ethernet Segment
3353 : : * - First Segment of inlined Ethernet data
3354 : : * - ... data continued ...
3355 : : * - Finishing Data Segment of pointer type
3356 : : */
3357 : 0 : ds = (MLX5_WQE_CSEG_SIZE +
3358 : : MLX5_WQE_ESEG_SIZE +
3359 : : MLX5_WQE_DSEG_SIZE +
3360 : 0 : txq->inlen_mode -
3361 : : MLX5_ESEG_MIN_INLINE_SIZE +
3362 : : MLX5_WQE_DSEG_SIZE +
3363 : 0 : MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
3364 [ # # # # : 0 : if (loc->wqe_free < ((ds + 3) / 4))
# # # # #
# # # #
# ]
3365 : : return MLX5_TXCMP_CODE_EXIT;
3366 : : /*
3367 : : * Build the ordinary SEND WQE:
3368 : : * - Control Segment
3369 : : * - Ethernet Segment, inline inlen_mode bytes
3370 : : * - Data Segment of pointer type
3371 : : */
3372 [ # # # # : 0 : wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
# # # # #
# # # #
# ]
3373 : : loc->wqe_last = wqe;
3374 : : mlx5_tx_cseg_init(txq, loc, wqe, ds,
3375 : : MLX5_OPCODE_SEND, olx);
3376 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3377 [ # # # # : 0 : dseg = mlx5_tx_eseg_data(txq, loc, wqe, vlan,
# # # # #
# # # #
# ]
3378 : : txq->inlen_mode,
3379 : : 0, olx);
3380 : 0 : dptr = rte_pktmbuf_mtod(loc->mbuf, uint8_t *) +
3381 : 0 : txq->inlen_mode - vlan;
3382 [ # # # # : 0 : inlen -= txq->inlen_mode;
# # # # #
# # # #
# ]
3383 : : mlx5_tx_dseg_ptr(txq, loc, dseg,
3384 : : dptr, inlen, olx);
3385 : : /*
3386 : : * WQE is built, update the loop parameters
3387 : : * and got to the next packet.
3388 : : */
3389 : 0 : txq->wqe_ci += (ds + 3) / 4;
3390 : 0 : loc->wqe_free -= (ds + 3) / 4;
3391 : : /* We have to store mbuf in elts.*/
3392 : : MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
3393 : 0 : txq->elts[txq->elts_head++ & txq->elts_m] =
3394 : : loc->mbuf;
3395 : 0 : --loc->elts_free;
3396 : : } else {
3397 : : uint8_t *dptr;
3398 : : unsigned int dlen;
3399 : :
3400 : : /*
3401 : : * Partially inlined packet data WQE, we have
3402 : : * some space in title WQEBB, we can fill it
3403 : : * with some packet data. It takes one WQEBB,
3404 : : * it is available, no extra space check:
3405 : : * - Control Segment, SEND opcode
3406 : : * - Ethernet Segment, no VLAN insertion
3407 : : * - MLX5_ESEG_MIN_INLINE_SIZE bytes of Data
3408 : : * - Data Segment, pointer type
3409 : : *
3410 : : * We also get here if VLAN insertion is not
3411 : : * supported by HW, the inline is enabled.
3412 : : */
3413 : 0 : single_part_inline:
3414 [ # # # # : 0 : wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
# # # # #
# # # #
# ]
3415 : : loc->wqe_last = wqe;
3416 : : mlx5_tx_cseg_init(txq, loc, wqe, 4,
3417 : : MLX5_OPCODE_SEND, olx);
3418 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3419 : : mlx5_tx_eseg_dmin(txq, loc, wqe, vlan, olx);
3420 : 0 : dptr = rte_pktmbuf_mtod(loc->mbuf, uint8_t *) +
3421 : 0 : MLX5_ESEG_MIN_INLINE_SIZE - vlan;
3422 : : /*
3423 : : * The length check is performed above, by
3424 : : * comparing with txq->inlen_send. We should
3425 : : * not get overflow here.
3426 : : */
3427 : : MLX5_ASSERT(inlen > MLX5_ESEG_MIN_INLINE_SIZE);
3428 [ # # # # : 0 : dlen = inlen - MLX5_ESEG_MIN_INLINE_SIZE;
# # # # #
# # # #
# ]
3429 : : mlx5_tx_dseg_ptr(txq, loc, &wqe->dseg[1],
3430 : : dptr, dlen, olx);
3431 : 0 : ++txq->wqe_ci;
3432 : 0 : --loc->wqe_free;
3433 : : /* We have to store mbuf in elts.*/
3434 : : MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
3435 : 0 : txq->elts[txq->elts_head++ & txq->elts_m] =
3436 : : loc->mbuf;
3437 : 0 : --loc->elts_free;
3438 : : }
3439 : : #ifdef MLX5_PMD_SOFT_COUNTERS
3440 : : /* Update sent data bytes counter. */
3441 : 0 : txq->stats.obytes += vlan +
3442 : 0 : rte_pktmbuf_data_len(loc->mbuf);
3443 : : #endif
3444 : : } else {
3445 : : /*
3446 : : * No inline at all, it means the CPU cycles saving
3447 : : * is prioritized at configuration, we should not
3448 : : * copy any packet data to WQE.
3449 : : *
3450 : : * SEND WQE, one WQEBB:
3451 : : * - Control Segment, SEND opcode
3452 : : * - Ethernet Segment, optional VLAN, no inline
3453 : : * - Data Segment, pointer type
3454 : : */
3455 : : single_no_inline:
3456 [ # # # # : 0 : wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
3457 : : loc->wqe_last = wqe;
3458 : : mlx5_tx_cseg_init(txq, loc, wqe, 3,
3459 : : MLX5_OPCODE_SEND, olx);
3460 : : rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3461 : : mlx5_tx_eseg_none(txq, loc, wqe, olx);
3462 : 0 : mlx5_tx_dseg_ptr
3463 : : (txq, loc, &wqe->dseg[0],
3464 : 0 : rte_pktmbuf_mtod(loc->mbuf, uint8_t *),
3465 [ # # # # : 0 : rte_pktmbuf_data_len(loc->mbuf), olx);
# # # # #
# # # # #
# # # # #
# # # ]
3466 : 0 : ++txq->wqe_ci;
3467 : 0 : --loc->wqe_free;
3468 : : /*
3469 : : * We should not store mbuf pointer in elts
3470 : : * if no inlining is configured, this is done
3471 : : * by calling routine in a batch copy.
3472 : : */
3473 : : if (MLX5_TXOFF_CONFIG(INLINE))
3474 : 0 : txq->elts[txq->elts_head++ & txq->elts_m] =
3475 : : loc->mbuf;
3476 : 0 : --loc->elts_free;
3477 : : #ifdef MLX5_PMD_SOFT_COUNTERS
3478 : : /* Update sent data bytes counter. */
3479 : 0 : txq->stats.obytes += rte_pktmbuf_data_len(loc->mbuf);
3480 : 0 : if (MLX5_TXOFF_CONFIG(VLAN) &&
3481 [ # # # # : 0 : loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN)
# # # # #
# # # #
# ]
3482 : 0 : txq->stats.obytes +=
3483 : : sizeof(struct rte_vlan_hdr);
3484 : : #endif
3485 : : }
3486 : 0 : ++loc->pkts_sent;
3487 : 0 : --pkts_n;
3488 [ # # # # : 0 : if (unlikely(!pkts_n || !loc->elts_free || !loc->wqe_free))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
3489 : : return MLX5_TXCMP_CODE_EXIT;
3490 : 0 : loc->mbuf = *pkts++;
3491 [ # # # # : 0 : if (pkts_n > 1)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
3492 : 0 : rte_prefetch0(*pkts);
3493 : : ret = mlx5_tx_able_to_empw(txq, loc, olx, true);
3494 [ # # # # : 0 : if (unlikely(ret != MLX5_TXCMP_CODE_SINGLE))
# # # # #
# # # #
# ]
3495 : : return ret;
3496 : : }
3497 : : MLX5_ASSERT(false);
3498 : : }
3499 : :
3500 : : static __rte_always_inline enum mlx5_txcmp_code
3501 : : mlx5_tx_burst_single(struct mlx5_txq_data *__rte_restrict txq,
3502 : : struct rte_mbuf **__rte_restrict pkts,
3503 : : unsigned int pkts_n,
3504 : : struct mlx5_txq_local *__rte_restrict loc,
3505 : : unsigned int olx)
3506 : : {
3507 : : enum mlx5_txcmp_code ret;
3508 : :
3509 : : ret = mlx5_tx_able_to_empw(txq, loc, olx, false);
3510 : : if (ret == MLX5_TXCMP_CODE_SINGLE)
3511 : 0 : goto ordinary_send;
3512 : : MLX5_ASSERT(ret == MLX5_TXCMP_CODE_EMPW);
3513 : : for (;;) {
3514 : : /* Optimize for inline/no inline eMPW send. */
3515 : : ret = (MLX5_TXOFF_CONFIG(INLINE)) ?
3516 : : mlx5_tx_burst_empw_inline
3517 : : (txq, pkts, pkts_n, loc, olx) :
3518 : : mlx5_tx_burst_empw_simple
3519 : : (txq, pkts, pkts_n, loc, olx);
3520 [ # # # # : 0 : if (ret != MLX5_TXCMP_CODE_SINGLE)
# # ]
3521 : : return ret;
3522 : : /* The resources to send one packet should remain. */
3523 : : MLX5_ASSERT(loc->elts_free && loc->wqe_free);
3524 : 0 : ordinary_send:
3525 : : ret = mlx5_tx_burst_single_send(txq, pkts, pkts_n, loc, olx);
3526 : : MLX5_ASSERT(ret != MLX5_TXCMP_CODE_SINGLE);
3527 [ # # # # : 0 : if (ret != MLX5_TXCMP_CODE_EMPW)
# # # # #
# # # #
# ]
3528 : : return ret;
3529 : : /* The resources to send one packet should remain. */
3530 : : MLX5_ASSERT(loc->elts_free && loc->wqe_free);
3531 : : }
3532 : : }
3533 : :
3534 : : /**
3535 : : * DPDK Tx callback template. This is configured template used to generate
3536 : : * routines optimized for specified offload setup.
3537 : : * One of this generated functions is chosen at SQ configuration time.
3538 : : *
3539 : : * @param txq
3540 : : * Generic pointer to TX queue structure.
3541 : : * @param[in] pkts
3542 : : * Packets to transmit.
3543 : : * @param pkts_n
3544 : : * Number of packets in array.
3545 : : * @param olx
3546 : : * Configured offloads mask, presents the bits of MLX5_TXOFF_CONFIG_xxx
3547 : : * values. Should be static to take compile time static configuration
3548 : : * advantages.
3549 : : *
3550 : : * @return
3551 : : * Number of packets successfully transmitted (<= pkts_n).
3552 : : */
3553 : : static __rte_always_inline uint16_t
3554 : : mlx5_tx_burst_tmpl(struct mlx5_txq_data *__rte_restrict txq,
3555 : : struct rte_mbuf **__rte_restrict pkts,
3556 : : uint16_t pkts_n,
3557 : : unsigned int olx)
3558 : : {
3559 : : struct mlx5_txq_local loc;
3560 : : enum mlx5_txcmp_code ret;
3561 : : unsigned int part;
3562 : :
3563 : : MLX5_ASSERT(txq->elts_s >= (uint16_t)(txq->elts_head - txq->elts_tail));
3564 : : MLX5_ASSERT(txq->wqe_s >= (uint16_t)(txq->wqe_ci - txq->wqe_pi));
3565 [ # # # # : 0 : if (unlikely(!pkts_n))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
3566 : : return 0;
3567 : : if (MLX5_TXOFF_CONFIG(INLINE))
3568 : : loc.mbuf_free = 0;
3569 : : loc.pkts_sent = 0;
3570 : : loc.pkts_copy = 0;
3571 : : loc.wqe_last = NULL;
3572 : :
3573 : 0 : send_loop:
3574 : : loc.pkts_loop = loc.pkts_sent;
3575 : : /*
3576 : : * Check if there are some CQEs, if any:
3577 : : * - process an encountered errors
3578 : : * - process the completed WQEs
3579 : : * - free related mbufs
3580 : : * - doorbell the NIC about processed CQEs
3581 : : */
3582 : 0 : rte_prefetch0(*(pkts + loc.pkts_sent));
3583 : 0 : mlx5_tx_handle_completion(txq, olx);
3584 : : /*
3585 : : * Calculate the number of available resources - elts and WQEs.
3586 : : * There are two possible different scenarios:
3587 : : * - no data inlining into WQEs, one WQEBB may contains up to
3588 : : * four packets, in this case elts become scarce resource
3589 : : * - data inlining into WQEs, one packet may require multiple
3590 : : * WQEBBs, the WQEs become the limiting factor.
3591 : : */
3592 : : MLX5_ASSERT(txq->elts_s >= (uint16_t)(txq->elts_head - txq->elts_tail));
3593 : 0 : loc.elts_free = txq->elts_s -
3594 : 0 : (uint16_t)(txq->elts_head - txq->elts_tail);
3595 : : MLX5_ASSERT(txq->wqe_s >= (uint16_t)(txq->wqe_ci - txq->wqe_pi));
3596 : 0 : loc.wqe_free = txq->wqe_s -
3597 : 0 : (uint16_t)(txq->wqe_ci - txq->wqe_pi);
3598 [ # # # # : 0 : if (unlikely(!loc.elts_free || !loc.wqe_free))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
3599 : 0 : goto burst_exit;
3600 : : for (;;) {
3601 : : /*
3602 : : * Fetch the packet from array. Usually this is the first
3603 : : * packet in series of multi/single segment packets.
3604 : : */
3605 : 0 : loc.mbuf = *(pkts + loc.pkts_sent);
3606 : : /* Dedicated branch for multi-segment packets. */
3607 : 0 : if (MLX5_TXOFF_CONFIG(MULTI) &&
3608 [ # # # # : 0 : unlikely(NB_SEGS(loc.mbuf) > 1)) {
# # # # #
# # # ]
3609 : : /*
3610 : : * Multi-segment packet encountered.
3611 : : * Hardware is able to process it only
3612 : : * with SEND/TSO opcodes, one packet
3613 : : * per WQE, do it in dedicated routine.
3614 : : */
3615 : 0 : enter_send_multi:
3616 : : MLX5_ASSERT(loc.pkts_sent >= loc.pkts_copy);
3617 : 0 : part = loc.pkts_sent - loc.pkts_copy;
3618 [ # # # # : 0 : if (!MLX5_TXOFF_CONFIG(INLINE) && part) {
# # ]
3619 : : /*
3620 : : * There are some single-segment mbufs not
3621 : : * stored in elts. The mbufs must be in the
3622 : : * same order as WQEs, so we must copy the
3623 : : * mbufs to elts here, before the coming
3624 : : * multi-segment packet mbufs is appended.
3625 : : */
3626 [ # # # # : 0 : mlx5_tx_copy_elts(txq, pkts + loc.pkts_copy,
# # ]
3627 : : part, olx);
3628 : : loc.pkts_copy = loc.pkts_sent;
3629 : : }
3630 : : MLX5_ASSERT(pkts_n > loc.pkts_sent);
3631 : 0 : ret = mlx5_tx_burst_mseg(txq, pkts, pkts_n, &loc, olx);
3632 : : if (!MLX5_TXOFF_CONFIG(INLINE))
3633 : : loc.pkts_copy = loc.pkts_sent;
3634 : : /*
3635 : : * These returned code checks are supposed
3636 : : * to be optimized out due to routine inlining.
3637 : : */
3638 [ # # # # : 0 : if (ret == MLX5_TXCMP_CODE_EXIT) {
# # # # #
# # # ]
3639 : : /*
3640 : : * The routine returns this code when
3641 : : * all packets are sent or there is no
3642 : : * enough resources to complete request.
3643 : : */
3644 : : break;
3645 : : }
3646 [ # # # # : 0 : if (ret == MLX5_TXCMP_CODE_ERROR) {
# # # # #
# # # ]
3647 : : /*
3648 : : * The routine returns this code when some error
3649 : : * in the incoming packets format occurred.
3650 : : */
3651 : 0 : txq->stats.oerrors++;
3652 : 0 : break;
3653 : : }
3654 [ # # # # : 0 : if (ret == MLX5_TXCMP_CODE_SINGLE) {
# # # # #
# # # ]
3655 : : /*
3656 : : * The single-segment packet was encountered
3657 : : * in the array, try to send it with the
3658 : : * best optimized way, possible engaging eMPW.
3659 : : */
3660 : 0 : goto enter_send_single;
3661 : : }
3662 : : if (MLX5_TXOFF_CONFIG(TSO) &&
3663 : : ret == MLX5_TXCMP_CODE_TSO) {
3664 : : /*
3665 : : * The single-segment TSO packet was
3666 : : * encountered in the array.
3667 : : */
3668 : 0 : goto enter_send_tso;
3669 : : }
3670 : : /* We must not get here. Something is going wrong. */
3671 : : MLX5_ASSERT(false);
3672 : : txq->stats.oerrors++;
3673 : : break;
3674 : : }
3675 : : /* Dedicated branch for single-segment TSO packets. */
3676 : 0 : if (MLX5_TXOFF_CONFIG(TSO) &&
3677 [ # # # # : 0 : unlikely(loc.mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG)) {
# # # # #
# # # ]
3678 : : /*
3679 : : * TSO might require special way for inlining
3680 : : * (dedicated parameters) and is sent with
3681 : : * MLX5_OPCODE_TSO opcode only, provide this
3682 : : * in dedicated branch.
3683 : : */
3684 : 0 : enter_send_tso:
3685 : : MLX5_ASSERT(NB_SEGS(loc.mbuf) == 1);
3686 : : MLX5_ASSERT(pkts_n > loc.pkts_sent);
3687 : 0 : ret = mlx5_tx_burst_tso(txq, pkts, pkts_n, &loc, olx);
3688 : : /*
3689 : : * These returned code checks are supposed
3690 : : * to be optimized out due to routine inlining.
3691 : : */
3692 [ # # # # : 0 : if (ret == MLX5_TXCMP_CODE_EXIT)
# # # # #
# # # ]
3693 : : break;
3694 [ # # # # : 0 : if (ret == MLX5_TXCMP_CODE_ERROR) {
# # # # #
# # # ]
3695 : 0 : txq->stats.oerrors++;
3696 : 0 : break;
3697 : : }
3698 [ # # # # : 0 : if (ret == MLX5_TXCMP_CODE_SINGLE)
# # # # #
# # # ]
3699 : 0 : goto enter_send_single;
3700 : : if (MLX5_TXOFF_CONFIG(MULTI) &&
3701 : : ret == MLX5_TXCMP_CODE_MULTI) {
3702 : : /*
3703 : : * The multi-segment packet was
3704 : : * encountered in the array.
3705 : : */
3706 : 0 : goto enter_send_multi;
3707 : : }
3708 : : /* We must not get here. Something is going wrong. */
3709 : : MLX5_ASSERT(false);
3710 : : txq->stats.oerrors++;
3711 : : break;
3712 : : }
3713 : : /*
3714 : : * The dedicated branch for the single-segment packets
3715 : : * without TSO. Often these ones can be sent using
3716 : : * MLX5_OPCODE_EMPW with multiple packets in one WQE.
3717 : : * The routine builds the WQEs till it encounters
3718 : : * the TSO or multi-segment packet (in case if these
3719 : : * offloads are requested at SQ configuration time).
3720 : : */
3721 : 0 : enter_send_single:
3722 : : MLX5_ASSERT(pkts_n > loc.pkts_sent);
3723 [ # # # # : 0 : ret = mlx5_tx_burst_single(txq, pkts, pkts_n, &loc, olx);
# # # # #
# # # #
# ]
3724 : : /*
3725 : : * These returned code checks are supposed
3726 : : * to be optimized out due to routine inlining.
3727 : : */
3728 [ # # # # : 0 : if (ret == MLX5_TXCMP_CODE_EXIT)
# # # # #
# # # # #
# # # # #
# ]
3729 : : break;
3730 [ # # # # : 0 : if (ret == MLX5_TXCMP_CODE_ERROR) {
# # # # #
# ]
3731 : 0 : txq->stats.oerrors++;
3732 : 0 : break;
3733 : : }
3734 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(MULTI) &&
# # # # #
# # # ]
3735 : : ret == MLX5_TXCMP_CODE_MULTI) {
3736 : : /*
3737 : : * The multi-segment packet was
3738 : : * encountered in the array.
3739 : : */
3740 : 0 : goto enter_send_multi;
3741 : : }
3742 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(TSO) &&
# # # # #
# # # ]
3743 : : ret == MLX5_TXCMP_CODE_TSO) {
3744 : : /*
3745 : : * The single-segment TSO packet was
3746 : : * encountered in the array.
3747 : : */
3748 : 0 : goto enter_send_tso;
3749 : : }
3750 : : /* We must not get here. Something is going wrong. */
3751 : : MLX5_ASSERT(false);
3752 : 0 : txq->stats.oerrors++;
3753 : 0 : break;
3754 : : }
3755 : : /*
3756 : : * Main Tx loop is completed, do the rest:
3757 : : * - set completion request if thresholds are reached
3758 : : * - doorbell the hardware
3759 : : * - copy the rest of mbufs to elts (if any)
3760 : : */
3761 : : MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE) ||
3762 : : loc.pkts_sent >= loc.pkts_copy);
3763 : : /* Take a shortcut if nothing is sent. */
3764 [ # # # # : 0 : if (unlikely(loc.pkts_sent == loc.pkts_loop))
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
3765 : 0 : goto burst_exit;
3766 : : /* Request CQE generation if limits are reached. */
3767 : : if (MLX5_TXOFF_CONFIG(TXPP) && __rte_trace_point_fp_is_enabled())
3768 : : mlx5_tx_request_completion_trace(txq, &loc, olx);
3769 : : else
3770 : : mlx5_tx_request_completion(txq, &loc, olx);
3771 : : /*
3772 : : * Ring QP doorbell immediately after WQE building completion
3773 : : * to improve latencies. The pure software related data treatment
3774 : : * can be completed after doorbell. Tx CQEs for this SQ are
3775 : : * processed in this thread only by the polling.
3776 : : *
3777 : : * The rdma core library can map doorbell register in two ways,
3778 : : * depending on the environment variable "MLX5_SHUT_UP_BF":
3779 : : *
3780 : : * - as regular cached memory, the variable is either missing or
3781 : : * set to zero. This type of mapping may cause the significant
3782 : : * doorbell register writing latency and requires explicit memory
3783 : : * write barrier to mitigate this issue and prevent write combining.
3784 : : *
3785 : : * - as non-cached memory, the variable is present and set to not "0"
3786 : : * value. This type of mapping may cause performance impact under
3787 : : * heavy loading conditions but the explicit write memory barrier is
3788 : : * not required and it may improve core performance.
3789 : : *
3790 : : * - the legacy behaviour (prior 19.08 release) was to use some
3791 : : * heuristics to decide whether write memory barrier should
3792 : : * be performed. This behavior is supported with specifying
3793 : : * tx_db_nc=2, write barrier is skipped if application provides
3794 : : * the full recommended burst of packets, it supposes the next
3795 : : * packets are coming and the write barrier will be issued on
3796 : : * the next burst (after descriptor writing, at least).
3797 : : */
3798 : 0 : mlx5_doorbell_ring(mlx5_tx_bfreg(txq),
3799 : 0 : *(volatile uint64_t *)loc.wqe_last, txq->wqe_ci,
3800 [ # # # # : 0 : txq->qp_db, !txq->db_nc &&
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
3801 [ # # # # : 0 : (!txq->db_heu || pkts_n % MLX5_TX_DEFAULT_BURST));
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
3802 : : /* Not all of the mbufs may be stored into elts yet. */
3803 : : part = MLX5_TXOFF_CONFIG(INLINE) ? 0 : loc.pkts_sent - loc.pkts_copy;
3804 [ # # # # : 0 : if (!MLX5_TXOFF_CONFIG(INLINE) && part) {
# # # # #
# # # # #
# # ]
3805 : : /*
3806 : : * There are some single-segment mbufs not stored in elts.
3807 : : * It can be only if the last packet was single-segment.
3808 : : * The copying is gathered into one place due to it is
3809 : : * a good opportunity to optimize that with SIMD.
3810 : : * Unfortunately if inlining is enabled the gaps in pointer
3811 : : * array may happen due to early freeing of the inlined mbufs.
3812 : : */
3813 [ # # # # : 0 : mlx5_tx_copy_elts(txq, pkts + loc.pkts_copy, part, olx);
# # # # #
# # # # #
# # ]
3814 : : loc.pkts_copy = loc.pkts_sent;
3815 : : }
3816 : : MLX5_ASSERT(txq->elts_s >= (uint16_t)(txq->elts_head - txq->elts_tail));
3817 : : MLX5_ASSERT(txq->wqe_s >= (uint16_t)(txq->wqe_ci - txq->wqe_pi));
3818 [ # # # # : 0 : if (pkts_n > loc.pkts_sent) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# ]
3819 : : /*
3820 : : * If burst size is large there might be no enough CQE
3821 : : * fetched from completion queue and no enough resources
3822 : : * freed to send all the packets.
3823 : : */
3824 : 0 : goto send_loop;
3825 : : }
3826 : 0 : burst_exit:
3827 : : #ifdef MLX5_PMD_SOFT_COUNTERS
3828 : : /* Increment sent packets counter. */
3829 : 0 : txq->stats.opackets += loc.pkts_sent;
3830 : : #endif
3831 [ # # # # : 0 : if (MLX5_TXOFF_CONFIG(INLINE) && loc.mbuf_free)
# # # # #
# # # #
# ]
3832 : 0 : __mlx5_tx_free_mbuf(txq, pkts, loc.mbuf_free, olx);
3833 : : /* Trace productive bursts only. */
3834 : : if (__rte_trace_point_fp_is_enabled() && loc.pkts_sent)
3835 : : rte_pmd_mlx5_trace_tx_exit(mlx5_read_pcibar_clock_from_txq(txq),
3836 : : loc.pkts_sent, pkts_n);
3837 : : return loc.pkts_sent;
3838 : : }
3839 : :
3840 : : /**
3841 : : * Check whether given TxQ is external.
3842 : : *
3843 : : * @param dev
3844 : : * Pointer to Ethernet device.
3845 : : * @param queue_idx
3846 : : * Tx queue index.
3847 : : *
3848 : : * @return
3849 : : * True if is external TxQ, otherwise false.
3850 : : */
3851 : : static __rte_always_inline bool
3852 : : mlx5_is_external_txq(struct rte_eth_dev *dev, uint16_t queue_idx)
3853 : : {
3854 : 0 : struct mlx5_priv *priv = dev->data->dev_private;
3855 : : struct mlx5_external_q *txq;
3856 : :
3857 [ # # # # ]: 0 : if (!priv->ext_txqs || queue_idx < MLX5_EXTERNAL_TX_QUEUE_ID_MIN)
3858 : : return false;
3859 : 0 : txq = &priv->ext_txqs[queue_idx - MLX5_EXTERNAL_TX_QUEUE_ID_MIN];
3860 [ # # ]: 0 : return !!rte_atomic_load_explicit(&txq->refcnt, rte_memory_order_relaxed);
3861 : : }
3862 : :
3863 : : #endif /* RTE_PMD_MLX5_TX_H_ */
|