xref: /dpdk/drivers/net/mlx5/mlx5_tx.h (revision 02932480ae82d7ed3c207f02cc40b508cdda6ded)
1377b69fbSMichael Baum /* SPDX-License-Identifier: BSD-3-Clause
2377b69fbSMichael Baum  * Copyright 2021 6WIND S.A.
3377b69fbSMichael Baum  * Copyright 2021 Mellanox Technologies, Ltd
4377b69fbSMichael Baum  */
5377b69fbSMichael Baum 
6377b69fbSMichael Baum #ifndef RTE_PMD_MLX5_TX_H_
7377b69fbSMichael Baum #define RTE_PMD_MLX5_TX_H_
8377b69fbSMichael Baum 
9377b69fbSMichael Baum #include <stdint.h>
10377b69fbSMichael Baum #include <sys/queue.h>
11377b69fbSMichael Baum 
12377b69fbSMichael Baum #include <rte_mbuf.h>
13377b69fbSMichael Baum #include <rte_mempool.h>
14377b69fbSMichael Baum #include <rte_common.h>
15377b69fbSMichael Baum #include <rte_spinlock.h>
16a1e910f5SViacheslav Ovsiienko #include <rte_trace_point.h>
17377b69fbSMichael Baum 
185dfa003dSMichael Baum #include <mlx5_common.h>
19377b69fbSMichael Baum #include <mlx5_common_mr.h>
20377b69fbSMichael Baum 
21377b69fbSMichael Baum #include "mlx5.h"
22377b69fbSMichael Baum #include "mlx5_autoconf.h"
23a1e910f5SViacheslav Ovsiienko #include "mlx5_rxtx.h"
24a1e910f5SViacheslav Ovsiienko #include "mlx5_trace.h"
25377b69fbSMichael Baum 
26256fb0e3SMichael Baum /* TX burst subroutines return codes. */
27256fb0e3SMichael Baum enum mlx5_txcmp_code {
28256fb0e3SMichael Baum 	MLX5_TXCMP_CODE_EXIT = 0,
29256fb0e3SMichael Baum 	MLX5_TXCMP_CODE_ERROR,
30256fb0e3SMichael Baum 	MLX5_TXCMP_CODE_SINGLE,
31256fb0e3SMichael Baum 	MLX5_TXCMP_CODE_MULTI,
32256fb0e3SMichael Baum 	MLX5_TXCMP_CODE_TSO,
33256fb0e3SMichael Baum 	MLX5_TXCMP_CODE_EMPW,
34256fb0e3SMichael Baum };
35256fb0e3SMichael Baum 
36256fb0e3SMichael Baum /*
37256fb0e3SMichael Baum  * These defines are used to configure Tx burst routine option set supported
38256fb0e3SMichael Baum  * at compile time. The not specified options are optimized out due to if
39256fb0e3SMichael Baum  * conditions can be explicitly calculated at compile time.
40256fb0e3SMichael Baum  * The offloads with bigger runtime check (require more CPU cycles toskip)
41256fb0e3SMichael Baum  * overhead should have the bigger index - this is needed to select the better
42256fb0e3SMichael Baum  * matching routine function if no exact match and some offloads are not
43256fb0e3SMichael Baum  * actually requested.
44256fb0e3SMichael Baum  */
45256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_MULTI (1u << 0) /* Multi-segment packets.*/
46256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_TSO (1u << 1) /* TCP send offload supported.*/
47256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_SWP (1u << 2) /* Tunnels/SW Parser offloads.*/
48256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_CSUM (1u << 3) /* Check Sums offloaded. */
49256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_INLINE (1u << 4) /* Data inlining supported. */
50256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_VLAN (1u << 5) /* VLAN insertion supported.*/
51256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_METADATA (1u << 6) /* Flow metadata. */
52256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_EMPW (1u << 8) /* Enhanced MPW supported.*/
53256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_MPW (1u << 9) /* Legacy MPW supported.*/
54256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_TXPP (1u << 10) /* Scheduling on timestamp.*/
55256fb0e3SMichael Baum 
56256fb0e3SMichael Baum /* The most common offloads groups. */
57256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_NONE 0
58256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG_FULL (MLX5_TXOFF_CONFIG_MULTI | \
59256fb0e3SMichael Baum 				MLX5_TXOFF_CONFIG_TSO | \
60256fb0e3SMichael Baum 				MLX5_TXOFF_CONFIG_SWP | \
61256fb0e3SMichael Baum 				MLX5_TXOFF_CONFIG_CSUM | \
62256fb0e3SMichael Baum 				MLX5_TXOFF_CONFIG_INLINE | \
63256fb0e3SMichael Baum 				MLX5_TXOFF_CONFIG_VLAN | \
64256fb0e3SMichael Baum 				MLX5_TXOFF_CONFIG_METADATA)
65256fb0e3SMichael Baum 
66256fb0e3SMichael Baum #define MLX5_TXOFF_CONFIG(mask) (olx & MLX5_TXOFF_CONFIG_##mask)
67256fb0e3SMichael Baum 
6879a876e3SMichael Baum #define MLX5_TXOFF_PRE_DECL(func) \
6979a876e3SMichael Baum uint16_t mlx5_tx_burst_##func(void *txq, \
7079a876e3SMichael Baum 			      struct rte_mbuf **pkts, \
7179a876e3SMichael Baum 			      uint16_t pkts_n)
7279a876e3SMichael Baum 
73256fb0e3SMichael Baum #define MLX5_TXOFF_DECL(func, olx) \
7479a876e3SMichael Baum uint16_t mlx5_tx_burst_##func(void *txq, \
75256fb0e3SMichael Baum 			      struct rte_mbuf **pkts, \
76256fb0e3SMichael Baum 			      uint16_t pkts_n) \
77256fb0e3SMichael Baum { \
78256fb0e3SMichael Baum 	return mlx5_tx_burst_tmpl((struct mlx5_txq_data *)txq, \
79256fb0e3SMichael Baum 		    pkts, pkts_n, (olx)); \
80256fb0e3SMichael Baum }
81256fb0e3SMichael Baum 
82377b69fbSMichael Baum /* Mbuf dynamic flag offset for inline. */
83377b69fbSMichael Baum extern uint64_t rte_net_mlx5_dynf_inline_mask;
84daa02b5cSOlivier Matz #define RTE_MBUF_F_TX_DYNF_NOINLINE rte_net_mlx5_dynf_inline_mask
85256fb0e3SMichael Baum 
8627595cd8STyler Retzlaff extern alignas(RTE_CACHE_LINE_SIZE) uint32_t mlx5_ptype_table[];
8727595cd8STyler Retzlaff extern alignas(RTE_CACHE_LINE_SIZE) uint8_t mlx5_cksum_table[1 << 10];
8827595cd8STyler Retzlaff extern alignas(RTE_CACHE_LINE_SIZE) uint8_t mlx5_swp_types_table[1 << 10];
89377b69fbSMichael Baum 
90377b69fbSMichael Baum struct mlx5_txq_stats {
91377b69fbSMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
92377b69fbSMichael Baum 	uint64_t opackets; /**< Total of successfully sent packets. */
93377b69fbSMichael Baum 	uint64_t obytes; /**< Total of successfully sent bytes. */
94377b69fbSMichael Baum #endif
95377b69fbSMichael Baum 	uint64_t oerrors; /**< Total number of failed transmitted packets. */
96377b69fbSMichael Baum };
97377b69fbSMichael Baum 
98377b69fbSMichael Baum /* TX queue send local data. */
99377b69fbSMichael Baum __extension__
100377b69fbSMichael Baum struct mlx5_txq_local {
101377b69fbSMichael Baum 	struct mlx5_wqe *wqe_last; /* last sent WQE pointer. */
102377b69fbSMichael Baum 	struct rte_mbuf *mbuf; /* first mbuf to process. */
103377b69fbSMichael Baum 	uint16_t pkts_copy; /* packets copied to elts. */
104377b69fbSMichael Baum 	uint16_t pkts_sent; /* packets sent. */
105377b69fbSMichael Baum 	uint16_t pkts_loop; /* packets sent on loop entry. */
106377b69fbSMichael Baum 	uint16_t elts_free; /* available elts remain. */
107377b69fbSMichael Baum 	uint16_t wqe_free; /* available wqe remain. */
108377b69fbSMichael Baum 	uint16_t mbuf_off; /* data offset in current mbuf. */
109377b69fbSMichael Baum 	uint16_t mbuf_nseg; /* number of remaining mbuf. */
110377b69fbSMichael Baum 	uint16_t mbuf_free; /* number of inline mbufs to free. */
111377b69fbSMichael Baum };
112377b69fbSMichael Baum 
113377b69fbSMichael Baum /* TX queue descriptor. */
114377b69fbSMichael Baum __extension__
11527595cd8STyler Retzlaff struct __rte_cache_aligned mlx5_txq_data {
116377b69fbSMichael Baum 	uint16_t elts_head; /* Current counter in (*elts)[]. */
117377b69fbSMichael Baum 	uint16_t elts_tail; /* Counter of first element awaiting completion. */
118377b69fbSMichael Baum 	uint16_t elts_comp; /* elts index since last completion request. */
119377b69fbSMichael Baum 	uint16_t elts_s; /* Number of mbuf elements. */
120377b69fbSMichael Baum 	uint16_t elts_m; /* Mask for mbuf elements indices. */
121377b69fbSMichael Baum 	/* Fields related to elts mbuf storage. */
122377b69fbSMichael Baum 	uint16_t wqe_ci; /* Consumer index for work queue. */
123377b69fbSMichael Baum 	uint16_t wqe_pi; /* Producer index for work queue. */
124377b69fbSMichael Baum 	uint16_t wqe_s; /* Number of WQ elements. */
125377b69fbSMichael Baum 	uint16_t wqe_m; /* Mask Number for WQ elements. */
126377b69fbSMichael Baum 	uint16_t wqe_comp; /* WQE index since last completion request. */
127377b69fbSMichael Baum 	uint16_t wqe_thres; /* WQE threshold to request completion in CQ. */
128377b69fbSMichael Baum 	/* WQ related fields. */
129377b69fbSMichael Baum 	uint16_t cq_ci; /* Consumer index for completion queue. */
130377b69fbSMichael Baum 	uint16_t cq_pi; /* Production index for completion queue. */
131377b69fbSMichael Baum 	uint16_t cqe_s; /* Number of CQ elements. */
132377b69fbSMichael Baum 	uint16_t cqe_m; /* Mask for CQ indices. */
133377b69fbSMichael Baum 	/* CQ related fields. */
134377b69fbSMichael Baum 	uint16_t elts_n:4; /* elts[] length (in log2). */
135377b69fbSMichael Baum 	uint16_t cqe_n:4; /* Number of CQ elements (in log2). */
136377b69fbSMichael Baum 	uint16_t wqe_n:4; /* Number of WQ elements (in log2). */
137377b69fbSMichael Baum 	uint16_t tso_en:1; /* When set hardware TSO is enabled. */
138377b69fbSMichael Baum 	uint16_t tunnel_en:1;
139377b69fbSMichael Baum 	/* When set TX offload for tunneled packets are supported. */
140377b69fbSMichael Baum 	uint16_t swp_en:1; /* Whether SW parser is enabled. */
141377b69fbSMichael Baum 	uint16_t vlan_en:1; /* VLAN insertion in WQE is supported. */
142377b69fbSMichael Baum 	uint16_t db_nc:1; /* Doorbell mapped to non-cached region. */
143377b69fbSMichael Baum 	uint16_t db_heu:1; /* Doorbell heuristic write barrier. */
1442f5122dfSViacheslav Ovsiienko 	uint16_t rt_timestamp:1; /* Realtime timestamp format. */
1452f5122dfSViacheslav Ovsiienko 	uint16_t wait_on_time:1; /* WQE with timestamp is supported. */
146377b69fbSMichael Baum 	uint16_t fast_free:1; /* mbuf fast free on Tx is enabled. */
147377b69fbSMichael Baum 	uint16_t inlen_send; /* Ordinary send data inline size. */
148377b69fbSMichael Baum 	uint16_t inlen_empw; /* eMPW max packet size to inline. */
149377b69fbSMichael Baum 	uint16_t inlen_mode; /* Minimal data length to inline. */
150ce306af6SJiawei Wang 	uint8_t tx_aggr_affinity; /* TxQ affinity configuration. */
151377b69fbSMichael Baum 	uint32_t qp_num_8s; /* QP number shifted by 8. */
152377b69fbSMichael Baum 	uint64_t offloads; /* Offloads for Tx Queue. */
153377b69fbSMichael Baum 	struct mlx5_mr_ctrl mr_ctrl; /* MR control descriptor. */
154377b69fbSMichael Baum 	struct mlx5_wqe *wqes; /* Work queue. */
155377b69fbSMichael Baum 	struct mlx5_wqe *wqes_end; /* Work queue array limit. */
156377b69fbSMichael Baum #ifdef RTE_LIBRTE_MLX5_DEBUG
157377b69fbSMichael Baum 	uint32_t *fcqs; /* Free completion queue (debug extended). */
158377b69fbSMichael Baum #else
159377b69fbSMichael Baum 	uint16_t *fcqs; /* Free completion queue. */
160377b69fbSMichael Baum #endif
161377b69fbSMichael Baum 	volatile struct mlx5_cqe *cqes; /* Completion queue. */
162377b69fbSMichael Baum 	volatile uint32_t *qp_db; /* Work queue doorbell. */
163377b69fbSMichael Baum 	volatile uint32_t *cq_db; /* Completion queue doorbell. */
164377b69fbSMichael Baum 	uint16_t port_id; /* Port ID of device. */
165377b69fbSMichael Baum 	uint16_t idx; /* Queue index. */
1662f5122dfSViacheslav Ovsiienko 	uint64_t rt_timemask; /* Scheduling timestamp mask. */
167377b69fbSMichael Baum 	uint64_t ts_mask; /* Timestamp flag dynamic mask. */
168a31aa37bSViacheslav Ovsiienko 	uint64_t ts_last; /* Last scheduled timestamp. */
169377b69fbSMichael Baum 	int32_t ts_offset; /* Timestamp field dynamic offset. */
170377b69fbSMichael Baum 	struct mlx5_dev_ctx_shared *sh; /* Shared context. */
171377b69fbSMichael Baum 	struct mlx5_txq_stats stats; /* TX queue counters. */
172773a7de2SRaja Zidane 	struct mlx5_txq_stats stats_reset; /* stats on last reset. */
1735dfa003dSMichael Baum 	struct mlx5_uar_data uar_data;
1740fc536d5SStephen Hemminger 	struct rte_mbuf *elts[];
175377b69fbSMichael Baum 	/* Storage for queued packets, must be the last field. */
17627595cd8STyler Retzlaff };
177377b69fbSMichael Baum 
178377b69fbSMichael Baum /* TX queue control descriptor. */
1790fc536d5SStephen Hemminger __extension__
180377b69fbSMichael Baum struct mlx5_txq_ctrl {
181377b69fbSMichael Baum 	LIST_ENTRY(mlx5_txq_ctrl) next; /* Pointer to the next element. */
182e12a0166STyler Retzlaff 	RTE_ATOMIC(uint32_t) refcnt; /* Reference counter. */
183377b69fbSMichael Baum 	unsigned int socket; /* CPU socket ID for allocations. */
184c06f77aeSMichael Baum 	bool is_hairpin; /* Whether TxQ type is Hairpin. */
185377b69fbSMichael Baum 	unsigned int max_inline_data; /* Max inline data. */
186377b69fbSMichael Baum 	unsigned int max_tso_header; /* Max TSO header size. */
187377b69fbSMichael Baum 	struct mlx5_txq_obj *obj; /* Verbs/DevX queue object. */
188377b69fbSMichael Baum 	struct mlx5_priv *priv; /* Back pointer to private data. */
189377b69fbSMichael Baum 	off_t uar_mmap_offset; /* UAR mmap offset for non-primary process. */
190377b69fbSMichael Baum 	uint16_t dump_file_n; /* Number of dump files. */
191377b69fbSMichael Baum 	struct rte_eth_hairpin_conf hairpin_conf; /* Hairpin configuration. */
192377b69fbSMichael Baum 	uint32_t hairpin_status; /* Hairpin binding status. */
193377b69fbSMichael Baum 	struct mlx5_txq_data txq; /* Data path structure. */
194377b69fbSMichael Baum 	/* Must be the last field in the structure, contains elts[]. */
195377b69fbSMichael Baum };
196377b69fbSMichael Baum 
197377b69fbSMichael Baum /* mlx5_txq.c */
198377b69fbSMichael Baum 
199377b69fbSMichael Baum int mlx5_tx_queue_start(struct rte_eth_dev *dev, uint16_t queue_id);
200377b69fbSMichael Baum int mlx5_tx_queue_stop(struct rte_eth_dev *dev, uint16_t queue_id);
201377b69fbSMichael Baum int mlx5_tx_queue_start_primary(struct rte_eth_dev *dev, uint16_t queue_id);
202377b69fbSMichael Baum int mlx5_tx_queue_stop_primary(struct rte_eth_dev *dev, uint16_t queue_id);
203377b69fbSMichael Baum int mlx5_tx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
204377b69fbSMichael Baum 			unsigned int socket, const struct rte_eth_txconf *conf);
205377b69fbSMichael Baum int mlx5_tx_hairpin_queue_setup
206377b69fbSMichael Baum 	(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
207377b69fbSMichael Baum 	 const struct rte_eth_hairpin_conf *hairpin_conf);
2087483341aSXueming Li void mlx5_tx_queue_release(struct rte_eth_dev *dev, uint16_t qid);
209377b69fbSMichael Baum int mlx5_tx_uar_init_secondary(struct rte_eth_dev *dev, int fd);
210377b69fbSMichael Baum void mlx5_tx_uar_uninit_secondary(struct rte_eth_dev *dev);
211377b69fbSMichael Baum int mlx5_txq_obj_verify(struct rte_eth_dev *dev);
212377b69fbSMichael Baum struct mlx5_txq_ctrl *mlx5_txq_new(struct rte_eth_dev *dev, uint16_t idx,
213377b69fbSMichael Baum 				   uint16_t desc, unsigned int socket,
214377b69fbSMichael Baum 				   const struct rte_eth_txconf *conf);
215377b69fbSMichael Baum struct mlx5_txq_ctrl *mlx5_txq_hairpin_new
216377b69fbSMichael Baum 	(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
217377b69fbSMichael Baum 	 const struct rte_eth_hairpin_conf *hairpin_conf);
218377b69fbSMichael Baum struct mlx5_txq_ctrl *mlx5_txq_get(struct rte_eth_dev *dev, uint16_t idx);
219377b69fbSMichael Baum int mlx5_txq_release(struct rte_eth_dev *dev, uint16_t idx);
220377b69fbSMichael Baum int mlx5_txq_releasable(struct rte_eth_dev *dev, uint16_t idx);
221377b69fbSMichael Baum int mlx5_txq_verify(struct rte_eth_dev *dev);
22226e1eaf2SDariusz Sosnowski int mlx5_txq_get_sqn(struct mlx5_txq_ctrl *txq);
223377b69fbSMichael Baum void txq_alloc_elts(struct mlx5_txq_ctrl *txq_ctrl);
224377b69fbSMichael Baum void txq_free_elts(struct mlx5_txq_ctrl *txq_ctrl);
225377b69fbSMichael Baum uint64_t mlx5_get_tx_port_offloads(struct rte_eth_dev *dev);
226377b69fbSMichael Baum void mlx5_txq_dynf_timestamp_set(struct rte_eth_dev *dev);
227ce306af6SJiawei Wang int mlx5_count_aggr_ports(struct rte_eth_dev *dev);
228ce306af6SJiawei Wang int mlx5_map_aggr_tx_affinity(struct rte_eth_dev *dev, uint16_t tx_queue_id,
229ce306af6SJiawei Wang 			      uint8_t affinity);
2301944fbc3SSuanming Mou int mlx5_ext_txq_verify(struct rte_eth_dev *dev);
2311944fbc3SSuanming Mou struct mlx5_external_q *mlx5_ext_txq_get(struct rte_eth_dev *dev, uint16_t idx);
232377b69fbSMichael Baum 
233377b69fbSMichael Baum /* mlx5_tx.c */
234377b69fbSMichael Baum 
235256fb0e3SMichael Baum void mlx5_tx_handle_completion(struct mlx5_txq_data *__rte_restrict txq,
236256fb0e3SMichael Baum 			       unsigned int olx __rte_unused);
237377b69fbSMichael Baum int mlx5_tx_descriptor_status(void *tx_queue, uint16_t offset);
238377b69fbSMichael Baum void mlx5_txq_info_get(struct rte_eth_dev *dev, uint16_t queue_id,
239377b69fbSMichael Baum 		       struct rte_eth_txq_info *qinfo);
240377b69fbSMichael Baum int mlx5_tx_burst_mode_get(struct rte_eth_dev *dev, uint16_t tx_queue_id,
241377b69fbSMichael Baum 			   struct rte_eth_burst_mode *mode);
242377b69fbSMichael Baum 
2434ac9ad07SMichael Baum /* mlx5_tx_empw.c */
24479a876e3SMichael Baum 
24579a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(full_empw);
24679a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(none_empw);
24779a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(md_empw);
24879a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mt_empw);
24979a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mtsc_empw);
25079a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mti_empw);
25179a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mtv_empw);
25279a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mtiv_empw);
25379a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(sc_empw);
25479a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(sci_empw);
25579a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(scv_empw);
25679a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(sciv_empw);
25779a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(i_empw);
25879a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(v_empw);
25979a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(iv_empw);
26079a876e3SMichael Baum 
2614ac9ad07SMichael Baum /* mlx5_tx_nompw.c */
2624ac9ad07SMichael Baum 
26379a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(full);
26479a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(none);
26579a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(md);
26679a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mt);
26779a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mtsc);
26879a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mti);
26979a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mtv);
27079a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mtiv);
27179a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(sc);
27279a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(sci);
27379a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(scv);
27479a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(sciv);
27579a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(i);
27679a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(v);
27779a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(iv);
27879a876e3SMichael Baum 
2794ac9ad07SMichael Baum /* mlx5_tx_txpp.c */
2804ac9ad07SMichael Baum 
28179a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(full_ts_nompw);
28279a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(full_ts_nompwi);
28379a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(full_ts);
28479a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(full_ts_noi);
28579a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(none_ts);
28679a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mdi_ts);
28779a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mti_ts);
28879a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mtiv_ts);
28979a876e3SMichael Baum 
2904ac9ad07SMichael Baum /* mlx5_tx_mpw.c */
2914ac9ad07SMichael Baum 
29279a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(none_mpw);
29379a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mci_mpw);
29479a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(mc_mpw);
29579a876e3SMichael Baum MLX5_TXOFF_PRE_DECL(i_mpw);
29679a876e3SMichael Baum 
2975dfa003dSMichael Baum static __rte_always_inline struct mlx5_uar_data *
298377b69fbSMichael Baum mlx5_tx_bfreg(struct mlx5_txq_data *txq)
299377b69fbSMichael Baum {
3005dfa003dSMichael Baum 	return &MLX5_PROC_PRIV(txq->port_id)->uar_table[txq->idx];
301377b69fbSMichael Baum }
302377b69fbSMichael Baum 
303377b69fbSMichael Baum /**
304377b69fbSMichael Baum  * Ring TX queue doorbell and flush the update by write memory barrier.
305377b69fbSMichael Baum  *
306377b69fbSMichael Baum  * @param txq
307377b69fbSMichael Baum  *   Pointer to TX queue structure.
308377b69fbSMichael Baum  * @param wqe
309377b69fbSMichael Baum  *   Pointer to the last WQE posted in the NIC.
310377b69fbSMichael Baum  */
311377b69fbSMichael Baum static __rte_always_inline void
312377b69fbSMichael Baum mlx5_tx_dbrec(struct mlx5_txq_data *txq, volatile struct mlx5_wqe *wqe)
313377b69fbSMichael Baum {
3145dfa003dSMichael Baum 	mlx5_doorbell_ring(mlx5_tx_bfreg(txq), *(volatile uint64_t *)wqe,
3155dfa003dSMichael Baum 			   txq->wqe_ci, txq->qp_db, 1);
316377b69fbSMichael Baum }
317377b69fbSMichael Baum 
318377b69fbSMichael Baum /**
319377b69fbSMichael Baum  * Convert timestamp from mbuf format to linear counter
320377b69fbSMichael Baum  * of Clock Queue completions (24 bits).
321377b69fbSMichael Baum  *
322377b69fbSMichael Baum  * @param sh
323377b69fbSMichael Baum  *   Pointer to the device shared context to fetch Tx
324377b69fbSMichael Baum  *   packet pacing timestamp and parameters.
325377b69fbSMichael Baum  * @param ts
326377b69fbSMichael Baum  *   Timestamp from mbuf to convert.
327377b69fbSMichael Baum  * @return
328377b69fbSMichael Baum  *   positive or zero value - completion ID to wait.
329377b69fbSMichael Baum  *   negative value - conversion error.
330377b69fbSMichael Baum  */
331377b69fbSMichael Baum static __rte_always_inline int32_t
332377b69fbSMichael Baum mlx5_txpp_convert_tx_ts(struct mlx5_dev_ctx_shared *sh, uint64_t mts)
333377b69fbSMichael Baum {
334377b69fbSMichael Baum 	uint64_t ts, ci;
335377b69fbSMichael Baum 	uint32_t tick;
336377b69fbSMichael Baum 
337377b69fbSMichael Baum 	do {
338377b69fbSMichael Baum 		/*
339377b69fbSMichael Baum 		 * Read atomically two uint64_t fields and compare lsb bits.
340377b69fbSMichael Baum 		 * It there is no match - the timestamp was updated in
341377b69fbSMichael Baum 		 * the service thread, data should be re-read.
342377b69fbSMichael Baum 		 */
343377b69fbSMichael Baum 		rte_compiler_barrier();
344e12a0166STyler Retzlaff 		ci = rte_atomic_load_explicit(&sh->txpp.ts.ci_ts, rte_memory_order_relaxed);
345e12a0166STyler Retzlaff 		ts = rte_atomic_load_explicit(&sh->txpp.ts.ts, rte_memory_order_relaxed);
346377b69fbSMichael Baum 		rte_compiler_barrier();
347377b69fbSMichael Baum 		if (!((ts ^ ci) << (64 - MLX5_CQ_INDEX_WIDTH)))
348377b69fbSMichael Baum 			break;
349377b69fbSMichael Baum 	} while (true);
350377b69fbSMichael Baum 	/* Perform the skew correction, positive value to send earlier. */
351377b69fbSMichael Baum 	mts -= sh->txpp.skew;
352377b69fbSMichael Baum 	mts -= ts;
353377b69fbSMichael Baum 	if (unlikely(mts >= UINT64_MAX / 2)) {
354377b69fbSMichael Baum 		/* We have negative integer, mts is in the past. */
355e12a0166STyler Retzlaff 		rte_atomic_fetch_add_explicit(&sh->txpp.err_ts_past,
356e12a0166STyler Retzlaff 				   1, rte_memory_order_relaxed);
357377b69fbSMichael Baum 		return -1;
358377b69fbSMichael Baum 	}
359377b69fbSMichael Baum 	tick = sh->txpp.tick;
360377b69fbSMichael Baum 	MLX5_ASSERT(tick);
361377b69fbSMichael Baum 	/* Convert delta to completions, round up. */
362377b69fbSMichael Baum 	mts = (mts + tick - 1) / tick;
363377b69fbSMichael Baum 	if (unlikely(mts >= (1 << MLX5_CQ_INDEX_WIDTH) / 2 - 1)) {
364377b69fbSMichael Baum 		/* We have mts is too distant future. */
365e12a0166STyler Retzlaff 		rte_atomic_fetch_add_explicit(&sh->txpp.err_ts_future,
366e12a0166STyler Retzlaff 				   1, rte_memory_order_relaxed);
367377b69fbSMichael Baum 		return -1;
368377b69fbSMichael Baum 	}
369377b69fbSMichael Baum 	mts <<= 64 - MLX5_CQ_INDEX_WIDTH;
370377b69fbSMichael Baum 	ci += mts;
371377b69fbSMichael Baum 	ci >>= 64 - MLX5_CQ_INDEX_WIDTH;
372377b69fbSMichael Baum 	return ci;
373377b69fbSMichael Baum }
374377b69fbSMichael Baum 
375256fb0e3SMichael Baum /**
37627918f0dSTim Martin  * Read real time clock counter directly from the device PCI BAR area.
37727918f0dSTim Martin  * The PCI BAR must be mapped to the process memory space at initialization.
37827918f0dSTim Martin  *
37927918f0dSTim Martin  * @param dev
38027918f0dSTim Martin  *   Device to read clock counter from
38127918f0dSTim Martin  *
38227918f0dSTim Martin  * @return
38327918f0dSTim Martin  *   0 - if HCA BAR is not supported or not mapped.
38427918f0dSTim Martin  *   !=0 - read 64-bit value of real-time in UTC formatv (nanoseconds)
38527918f0dSTim Martin  */
38627918f0dSTim Martin static __rte_always_inline uint64_t mlx5_read_pcibar_clock(struct rte_eth_dev *dev)
38727918f0dSTim Martin {
38827918f0dSTim Martin 	struct mlx5_proc_priv *ppriv = dev->process_private;
38927918f0dSTim Martin 
39027918f0dSTim Martin 	if (ppriv && ppriv->hca_bar) {
39127918f0dSTim Martin 		struct mlx5_priv *priv = dev->data->dev_private;
39227918f0dSTim Martin 		struct mlx5_dev_ctx_shared *sh = priv->sh;
39327918f0dSTim Martin 		uint64_t *hca_ptr = (uint64_t *)(ppriv->hca_bar) +
39427918f0dSTim Martin 				  __mlx5_64_off(initial_seg, real_time);
39527918f0dSTim Martin 		uint64_t __rte_atomic *ts_addr;
39627918f0dSTim Martin 		uint64_t ts;
39727918f0dSTim Martin 
39827918f0dSTim Martin 		ts_addr = (uint64_t __rte_atomic *)hca_ptr;
39927918f0dSTim Martin 		ts = rte_atomic_load_explicit(ts_addr, rte_memory_order_seq_cst);
40027918f0dSTim Martin 		ts = rte_be_to_cpu_64(ts);
40127918f0dSTim Martin 		ts = mlx5_txpp_convert_rx_ts(sh, ts);
40227918f0dSTim Martin 		return ts;
40327918f0dSTim Martin 	}
40427918f0dSTim Martin 	return 0;
40527918f0dSTim Martin }
40627918f0dSTim Martin 
407*02932480STim Martin static __rte_always_inline uint64_t mlx5_read_pcibar_clock_from_txq(struct mlx5_txq_data *txq)
408*02932480STim Martin {
409*02932480STim Martin 	struct mlx5_txq_ctrl *txq_ctrl = container_of(txq, struct mlx5_txq_ctrl, txq);
410*02932480STim Martin 	struct rte_eth_dev *dev = ETH_DEV(txq_ctrl->priv);
411*02932480STim Martin 
412*02932480STim Martin 	return mlx5_read_pcibar_clock(dev);
413*02932480STim Martin }
414*02932480STim Martin 
41527918f0dSTim Martin /**
416256fb0e3SMichael Baum  * Set Software Parser flags and offsets in Ethernet Segment of WQE.
417256fb0e3SMichael Baum  * Flags must be preliminary initialized to zero.
418256fb0e3SMichael Baum  *
419256fb0e3SMichael Baum  * @param loc
420256fb0e3SMichael Baum  *   Pointer to burst routine local context.
421256fb0e3SMichael Baum  * @param swp_flags
422256fb0e3SMichael Baum  *   Pointer to store Software Parser flags.
423256fb0e3SMichael Baum  * @param olx
424256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
425256fb0e3SMichael Baum  *   compile time and may be used for optimization.
426256fb0e3SMichael Baum  *
427256fb0e3SMichael Baum  * @return
428256fb0e3SMichael Baum  *   Software Parser offsets packed in dword.
429256fb0e3SMichael Baum  *   Software Parser flags are set by pointer.
430256fb0e3SMichael Baum  */
431256fb0e3SMichael Baum static __rte_always_inline uint32_t
432256fb0e3SMichael Baum txq_mbuf_to_swp(struct mlx5_txq_local *__rte_restrict loc,
433256fb0e3SMichael Baum 		uint8_t *swp_flags,
434256fb0e3SMichael Baum 		unsigned int olx)
435256fb0e3SMichael Baum {
436256fb0e3SMichael Baum 	uint64_t ol, tunnel;
437256fb0e3SMichael Baum 	unsigned int idx, off;
438256fb0e3SMichael Baum 	uint32_t set;
439256fb0e3SMichael Baum 
440256fb0e3SMichael Baum 	if (!MLX5_TXOFF_CONFIG(SWP))
441256fb0e3SMichael Baum 		return 0;
442256fb0e3SMichael Baum 	ol = loc->mbuf->ol_flags;
443daa02b5cSOlivier Matz 	tunnel = ol & RTE_MBUF_F_TX_TUNNEL_MASK;
444256fb0e3SMichael Baum 	/*
445256fb0e3SMichael Baum 	 * Check whether Software Parser is required.
446256fb0e3SMichael Baum 	 * Only customized tunnels may ask for.
447256fb0e3SMichael Baum 	 */
448daa02b5cSOlivier Matz 	if (likely(tunnel != RTE_MBUF_F_TX_TUNNEL_UDP && tunnel != RTE_MBUF_F_TX_TUNNEL_IP))
449256fb0e3SMichael Baum 		return 0;
450256fb0e3SMichael Baum 	/*
451256fb0e3SMichael Baum 	 * The index should have:
452daa02b5cSOlivier Matz 	 * bit[0:1] = RTE_MBUF_F_TX_L4_MASK
453daa02b5cSOlivier Matz 	 * bit[4] = RTE_MBUF_F_TX_IPV6
454daa02b5cSOlivier Matz 	 * bit[8] = RTE_MBUF_F_TX_OUTER_IPV6
455daa02b5cSOlivier Matz 	 * bit[9] = RTE_MBUF_F_TX_OUTER_UDP
456256fb0e3SMichael Baum 	 */
457daa02b5cSOlivier Matz 	idx = (ol & (RTE_MBUF_F_TX_L4_MASK | RTE_MBUF_F_TX_IPV6 | RTE_MBUF_F_TX_OUTER_IPV6)) >> 52;
458daa02b5cSOlivier Matz 	idx |= (tunnel == RTE_MBUF_F_TX_TUNNEL_UDP) ? (1 << 9) : 0;
459256fb0e3SMichael Baum 	*swp_flags = mlx5_swp_types_table[idx];
460256fb0e3SMichael Baum 	/*
461256fb0e3SMichael Baum 	 * Set offsets for SW parser. Since ConnectX-5, SW parser just
462256fb0e3SMichael Baum 	 * complements HW parser. SW parser starts to engage only if HW parser
463256fb0e3SMichael Baum 	 * can't reach a header. For the older devices, HW parser will not kick
464256fb0e3SMichael Baum 	 * in if any of SWP offsets is set. Therefore, all of the L3 offsets
465256fb0e3SMichael Baum 	 * should be set regardless of HW offload.
466256fb0e3SMichael Baum 	 */
467256fb0e3SMichael Baum 	off = loc->mbuf->outer_l2_len;
468daa02b5cSOlivier Matz 	if (MLX5_TXOFF_CONFIG(VLAN) && ol & RTE_MBUF_F_TX_VLAN)
469256fb0e3SMichael Baum 		off += sizeof(struct rte_vlan_hdr);
470256fb0e3SMichael Baum 	set = (off >> 1) << 8; /* Outer L3 offset. */
471256fb0e3SMichael Baum 	off += loc->mbuf->outer_l3_len;
472daa02b5cSOlivier Matz 	if (tunnel == RTE_MBUF_F_TX_TUNNEL_UDP)
473256fb0e3SMichael Baum 		set |= off >> 1; /* Outer L4 offset. */
474daa02b5cSOlivier Matz 	if (ol & (RTE_MBUF_F_TX_IPV4 | RTE_MBUF_F_TX_IPV6)) { /* Inner IP. */
475daa02b5cSOlivier Matz 		const uint64_t csum = ol & RTE_MBUF_F_TX_L4_MASK;
476256fb0e3SMichael Baum 			off += loc->mbuf->l2_len;
477256fb0e3SMichael Baum 		set |= (off >> 1) << 24; /* Inner L3 offset. */
478daa02b5cSOlivier Matz 		if (csum == RTE_MBUF_F_TX_TCP_CKSUM ||
479daa02b5cSOlivier Matz 		    csum == RTE_MBUF_F_TX_UDP_CKSUM ||
480daa02b5cSOlivier Matz 		    (MLX5_TXOFF_CONFIG(TSO) && ol & RTE_MBUF_F_TX_TCP_SEG)) {
481256fb0e3SMichael Baum 			off += loc->mbuf->l3_len;
482256fb0e3SMichael Baum 			set |= (off >> 1) << 16; /* Inner L4 offset. */
483256fb0e3SMichael Baum 		}
484256fb0e3SMichael Baum 	}
485256fb0e3SMichael Baum 	set = rte_cpu_to_le_32(set);
486256fb0e3SMichael Baum 	return set;
487256fb0e3SMichael Baum }
488256fb0e3SMichael Baum 
489256fb0e3SMichael Baum /**
490256fb0e3SMichael Baum  * Convert the Checksum offloads to Verbs.
491256fb0e3SMichael Baum  *
492256fb0e3SMichael Baum  * @param buf
493256fb0e3SMichael Baum  *   Pointer to the mbuf.
494256fb0e3SMichael Baum  *
495256fb0e3SMichael Baum  * @return
496256fb0e3SMichael Baum  *   Converted checksum flags.
497256fb0e3SMichael Baum  */
498256fb0e3SMichael Baum static __rte_always_inline uint8_t
499256fb0e3SMichael Baum txq_ol_cksum_to_cs(struct rte_mbuf *buf)
500256fb0e3SMichael Baum {
501256fb0e3SMichael Baum 	uint32_t idx;
502daa02b5cSOlivier Matz 	uint8_t is_tunnel = !!(buf->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK);
503daa02b5cSOlivier Matz 	const uint64_t ol_flags_mask = RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_L4_MASK |
504daa02b5cSOlivier Matz 				       RTE_MBUF_F_TX_IP_CKSUM | RTE_MBUF_F_TX_OUTER_IP_CKSUM;
505256fb0e3SMichael Baum 
506256fb0e3SMichael Baum 	/*
507256fb0e3SMichael Baum 	 * The index should have:
508daa02b5cSOlivier Matz 	 * bit[0] = RTE_MBUF_F_TX_TCP_SEG
509daa02b5cSOlivier Matz 	 * bit[2:3] = RTE_MBUF_F_TX_UDP_CKSUM, RTE_MBUF_F_TX_TCP_CKSUM
510daa02b5cSOlivier Matz 	 * bit[4] = RTE_MBUF_F_TX_IP_CKSUM
511daa02b5cSOlivier Matz 	 * bit[8] = RTE_MBUF_F_TX_OUTER_IP_CKSUM
512256fb0e3SMichael Baum 	 * bit[9] = tunnel
513256fb0e3SMichael Baum 	 */
514256fb0e3SMichael Baum 	idx = ((buf->ol_flags & ol_flags_mask) >> 50) | (!!is_tunnel << 9);
515256fb0e3SMichael Baum 	return mlx5_cksum_table[idx];
516256fb0e3SMichael Baum }
517256fb0e3SMichael Baum 
518256fb0e3SMichael Baum /**
519256fb0e3SMichael Baum  * Free the mbufs from the linear array of pointers.
520256fb0e3SMichael Baum  *
521256fb0e3SMichael Baum  * @param txq
522256fb0e3SMichael Baum  *   Pointer to Tx queue structure.
523256fb0e3SMichael Baum  * @param pkts
524256fb0e3SMichael Baum  *   Pointer to array of packets to be free.
525256fb0e3SMichael Baum  * @param pkts_n
526256fb0e3SMichael Baum  *   Number of packets to be freed.
527256fb0e3SMichael Baum  * @param olx
528256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
529256fb0e3SMichael Baum  *   compile time and may be used for optimization.
530256fb0e3SMichael Baum  */
531256fb0e3SMichael Baum static __rte_always_inline void
532256fb0e3SMichael Baum mlx5_tx_free_mbuf(struct mlx5_txq_data *__rte_restrict txq,
533256fb0e3SMichael Baum 		  struct rte_mbuf **__rte_restrict pkts,
534256fb0e3SMichael Baum 		  unsigned int pkts_n,
535256fb0e3SMichael Baum 		  unsigned int olx __rte_unused)
536256fb0e3SMichael Baum {
537256fb0e3SMichael Baum 	struct rte_mempool *pool = NULL;
538256fb0e3SMichael Baum 	struct rte_mbuf **p_free = NULL;
539256fb0e3SMichael Baum 	struct rte_mbuf *mbuf;
540256fb0e3SMichael Baum 	unsigned int n_free = 0;
541256fb0e3SMichael Baum 
542256fb0e3SMichael Baum 	/*
543256fb0e3SMichael Baum 	 * The implemented algorithm eliminates
544256fb0e3SMichael Baum 	 * copying pointers to temporary array
545256fb0e3SMichael Baum 	 * for rte_mempool_put_bulk() calls.
546256fb0e3SMichael Baum 	 */
547256fb0e3SMichael Baum 	MLX5_ASSERT(pkts);
548256fb0e3SMichael Baum 	MLX5_ASSERT(pkts_n);
549256fb0e3SMichael Baum 	/*
550256fb0e3SMichael Baum 	 * Free mbufs directly to the pool in bulk
551256fb0e3SMichael Baum 	 * if fast free offload is engaged
552256fb0e3SMichael Baum 	 */
553256fb0e3SMichael Baum 	if (!MLX5_TXOFF_CONFIG(MULTI) && txq->fast_free) {
554256fb0e3SMichael Baum 		mbuf = *pkts;
555256fb0e3SMichael Baum 		pool = mbuf->pool;
556256fb0e3SMichael Baum 		rte_mempool_put_bulk(pool, (void *)pkts, pkts_n);
557256fb0e3SMichael Baum 		return;
558256fb0e3SMichael Baum 	}
559256fb0e3SMichael Baum 	for (;;) {
560256fb0e3SMichael Baum 		for (;;) {
561256fb0e3SMichael Baum 			/*
562256fb0e3SMichael Baum 			 * Decrement mbuf reference counter, detach
563256fb0e3SMichael Baum 			 * indirect and external buffers if needed.
564256fb0e3SMichael Baum 			 */
565256fb0e3SMichael Baum 			mbuf = rte_pktmbuf_prefree_seg(*pkts);
566256fb0e3SMichael Baum 			if (likely(mbuf != NULL)) {
567256fb0e3SMichael Baum 				MLX5_ASSERT(mbuf == *pkts);
568256fb0e3SMichael Baum 				if (likely(n_free != 0)) {
569256fb0e3SMichael Baum 					if (unlikely(pool != mbuf->pool))
570256fb0e3SMichael Baum 						/* From different pool. */
571256fb0e3SMichael Baum 						break;
572256fb0e3SMichael Baum 				} else {
573256fb0e3SMichael Baum 					/* Start new scan array. */
574256fb0e3SMichael Baum 					pool = mbuf->pool;
575256fb0e3SMichael Baum 					p_free = pkts;
576256fb0e3SMichael Baum 				}
577256fb0e3SMichael Baum 				++n_free;
578256fb0e3SMichael Baum 				++pkts;
579256fb0e3SMichael Baum 				--pkts_n;
580256fb0e3SMichael Baum 				if (unlikely(pkts_n == 0)) {
581256fb0e3SMichael Baum 					mbuf = NULL;
582256fb0e3SMichael Baum 					break;
583256fb0e3SMichael Baum 				}
584256fb0e3SMichael Baum 			} else {
585256fb0e3SMichael Baum 				/*
586256fb0e3SMichael Baum 				 * This happens if mbuf is still referenced.
587256fb0e3SMichael Baum 				 * We can't put it back to the pool, skip.
588256fb0e3SMichael Baum 				 */
589256fb0e3SMichael Baum 				++pkts;
590256fb0e3SMichael Baum 				--pkts_n;
591256fb0e3SMichael Baum 				if (unlikely(n_free != 0))
592256fb0e3SMichael Baum 					/* There is some array to free.*/
593256fb0e3SMichael Baum 					break;
594256fb0e3SMichael Baum 				if (unlikely(pkts_n == 0))
595256fb0e3SMichael Baum 					/* Last mbuf, nothing to free. */
596256fb0e3SMichael Baum 					return;
597256fb0e3SMichael Baum 			}
598256fb0e3SMichael Baum 		}
599256fb0e3SMichael Baum 		for (;;) {
600256fb0e3SMichael Baum 			/*
601256fb0e3SMichael Baum 			 * This loop is implemented to avoid multiple
602256fb0e3SMichael Baum 			 * inlining of rte_mempool_put_bulk().
603256fb0e3SMichael Baum 			 */
604256fb0e3SMichael Baum 			MLX5_ASSERT(pool);
605256fb0e3SMichael Baum 			MLX5_ASSERT(p_free);
606256fb0e3SMichael Baum 			MLX5_ASSERT(n_free);
607256fb0e3SMichael Baum 			/*
608256fb0e3SMichael Baum 			 * Free the array of pre-freed mbufs
609256fb0e3SMichael Baum 			 * belonging to the same memory pool.
610256fb0e3SMichael Baum 			 */
611256fb0e3SMichael Baum 			rte_mempool_put_bulk(pool, (void *)p_free, n_free);
612256fb0e3SMichael Baum 			if (unlikely(mbuf != NULL)) {
613256fb0e3SMichael Baum 				/* There is the request to start new scan. */
614256fb0e3SMichael Baum 				pool = mbuf->pool;
615256fb0e3SMichael Baum 				p_free = pkts++;
616256fb0e3SMichael Baum 				n_free = 1;
617256fb0e3SMichael Baum 				--pkts_n;
618256fb0e3SMichael Baum 				if (likely(pkts_n != 0))
619256fb0e3SMichael Baum 					break;
620256fb0e3SMichael Baum 				/*
621256fb0e3SMichael Baum 				 * This is the last mbuf to be freed.
622256fb0e3SMichael Baum 				 * Do one more loop iteration to complete.
623256fb0e3SMichael Baum 				 * This is rare case of the last unique mbuf.
624256fb0e3SMichael Baum 				 */
625256fb0e3SMichael Baum 				mbuf = NULL;
626256fb0e3SMichael Baum 				continue;
627256fb0e3SMichael Baum 			}
628256fb0e3SMichael Baum 			if (likely(pkts_n == 0))
629256fb0e3SMichael Baum 				return;
630256fb0e3SMichael Baum 			n_free = 0;
631256fb0e3SMichael Baum 			break;
632256fb0e3SMichael Baum 		}
633256fb0e3SMichael Baum 	}
634256fb0e3SMichael Baum }
635256fb0e3SMichael Baum 
636256fb0e3SMichael Baum /**
637256fb0e3SMichael Baum  * No inline version to free buffers for optimal call
638256fb0e3SMichael Baum  * on the tx_burst completion.
639256fb0e3SMichael Baum  */
640256fb0e3SMichael Baum static __rte_noinline void
641256fb0e3SMichael Baum __mlx5_tx_free_mbuf(struct mlx5_txq_data *__rte_restrict txq,
642256fb0e3SMichael Baum 		    struct rte_mbuf **__rte_restrict pkts,
643256fb0e3SMichael Baum 		    unsigned int pkts_n,
644256fb0e3SMichael Baum 		    unsigned int olx __rte_unused)
645256fb0e3SMichael Baum {
646256fb0e3SMichael Baum 	mlx5_tx_free_mbuf(txq, pkts, pkts_n, olx);
647256fb0e3SMichael Baum }
648256fb0e3SMichael Baum 
649256fb0e3SMichael Baum /**
650256fb0e3SMichael Baum  * Free the mbuf from the elts ring buffer till new tail.
651256fb0e3SMichael Baum  *
652256fb0e3SMichael Baum  * @param txq
653256fb0e3SMichael Baum  *   Pointer to Tx queue structure.
654256fb0e3SMichael Baum  * @param tail
655256fb0e3SMichael Baum  *   Index in elts to free up to, becomes new elts tail.
656256fb0e3SMichael Baum  * @param olx
657256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
658256fb0e3SMichael Baum  *   compile time and may be used for optimization.
659256fb0e3SMichael Baum  */
660256fb0e3SMichael Baum static __rte_always_inline void
661256fb0e3SMichael Baum mlx5_tx_free_elts(struct mlx5_txq_data *__rte_restrict txq,
662256fb0e3SMichael Baum 		  uint16_t tail,
663256fb0e3SMichael Baum 		  unsigned int olx __rte_unused)
664256fb0e3SMichael Baum {
665256fb0e3SMichael Baum 	uint16_t n_elts = tail - txq->elts_tail;
666256fb0e3SMichael Baum 
667256fb0e3SMichael Baum 	MLX5_ASSERT(n_elts);
668256fb0e3SMichael Baum 	MLX5_ASSERT(n_elts <= txq->elts_s);
669256fb0e3SMichael Baum 	/*
670256fb0e3SMichael Baum 	 * Implement a loop to support ring buffer wraparound
671256fb0e3SMichael Baum 	 * with single inlining of mlx5_tx_free_mbuf().
672256fb0e3SMichael Baum 	 */
673256fb0e3SMichael Baum 	do {
674256fb0e3SMichael Baum 		unsigned int part;
675256fb0e3SMichael Baum 
676256fb0e3SMichael Baum 		part = txq->elts_s - (txq->elts_tail & txq->elts_m);
677256fb0e3SMichael Baum 		part = RTE_MIN(part, n_elts);
678256fb0e3SMichael Baum 		MLX5_ASSERT(part);
679256fb0e3SMichael Baum 		MLX5_ASSERT(part <= txq->elts_s);
680256fb0e3SMichael Baum 		mlx5_tx_free_mbuf(txq,
681256fb0e3SMichael Baum 				  &txq->elts[txq->elts_tail & txq->elts_m],
682256fb0e3SMichael Baum 				  part, olx);
683256fb0e3SMichael Baum 		txq->elts_tail += part;
684256fb0e3SMichael Baum 		n_elts -= part;
685256fb0e3SMichael Baum 	} while (n_elts);
686256fb0e3SMichael Baum }
687256fb0e3SMichael Baum 
688256fb0e3SMichael Baum /**
689256fb0e3SMichael Baum  * Store the mbuf being sent into elts ring buffer.
690256fb0e3SMichael Baum  * On Tx completion these mbufs will be freed.
691256fb0e3SMichael Baum  *
692256fb0e3SMichael Baum  * @param txq
693256fb0e3SMichael Baum  *   Pointer to Tx queue structure.
694256fb0e3SMichael Baum  * @param pkts
695256fb0e3SMichael Baum  *   Pointer to array of packets to be stored.
696256fb0e3SMichael Baum  * @param pkts_n
697256fb0e3SMichael Baum  *   Number of packets to be stored.
698256fb0e3SMichael Baum  * @param olx
699256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
700256fb0e3SMichael Baum  *   compile time and may be used for optimization.
701256fb0e3SMichael Baum  */
702256fb0e3SMichael Baum static __rte_always_inline void
703256fb0e3SMichael Baum mlx5_tx_copy_elts(struct mlx5_txq_data *__rte_restrict txq,
704256fb0e3SMichael Baum 		  struct rte_mbuf **__rte_restrict pkts,
705256fb0e3SMichael Baum 		  unsigned int pkts_n,
706256fb0e3SMichael Baum 		  unsigned int olx __rte_unused)
707256fb0e3SMichael Baum {
708256fb0e3SMichael Baum 	unsigned int part;
709256fb0e3SMichael Baum 	struct rte_mbuf **elts = (struct rte_mbuf **)txq->elts;
710256fb0e3SMichael Baum 
711256fb0e3SMichael Baum 	MLX5_ASSERT(pkts);
712256fb0e3SMichael Baum 	MLX5_ASSERT(pkts_n);
713256fb0e3SMichael Baum 	part = txq->elts_s - (txq->elts_head & txq->elts_m);
714256fb0e3SMichael Baum 	MLX5_ASSERT(part);
715256fb0e3SMichael Baum 	MLX5_ASSERT(part <= txq->elts_s);
716256fb0e3SMichael Baum 	/* This code is a good candidate for vectorizing with SIMD. */
717256fb0e3SMichael Baum 	rte_memcpy((void *)(elts + (txq->elts_head & txq->elts_m)),
718256fb0e3SMichael Baum 		   (void *)pkts,
719256fb0e3SMichael Baum 		   RTE_MIN(part, pkts_n) * sizeof(struct rte_mbuf *));
720256fb0e3SMichael Baum 	txq->elts_head += pkts_n;
721256fb0e3SMichael Baum 	if (unlikely(part < pkts_n))
722256fb0e3SMichael Baum 		/* The copy is wrapping around the elts array. */
723256fb0e3SMichael Baum 		rte_memcpy((void *)elts, (void *)(pkts + part),
724256fb0e3SMichael Baum 			   (pkts_n - part) * sizeof(struct rte_mbuf *));
725256fb0e3SMichael Baum }
726256fb0e3SMichael Baum 
727256fb0e3SMichael Baum /**
728256fb0e3SMichael Baum  * Check if the completion request flag should be set in the last WQE.
729256fb0e3SMichael Baum  * Both pushed mbufs and WQEs are monitored and the completion request
730256fb0e3SMichael Baum  * flag is set if any of thresholds is reached.
731256fb0e3SMichael Baum  *
732256fb0e3SMichael Baum  * @param txq
733256fb0e3SMichael Baum  *   Pointer to TX queue structure.
734256fb0e3SMichael Baum  * @param loc
735256fb0e3SMichael Baum  *   Pointer to burst routine local context.
736256fb0e3SMichael Baum  * @param olx
737256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
738256fb0e3SMichael Baum  *   compile time and may be used for optimization.
739256fb0e3SMichael Baum  */
740256fb0e3SMichael Baum static __rte_always_inline void
741256fb0e3SMichael Baum mlx5_tx_request_completion(struct mlx5_txq_data *__rte_restrict txq,
742256fb0e3SMichael Baum 			   struct mlx5_txq_local *__rte_restrict loc,
743256fb0e3SMichael Baum 			   unsigned int olx)
744256fb0e3SMichael Baum {
745256fb0e3SMichael Baum 	uint16_t head = txq->elts_head;
746256fb0e3SMichael Baum 	unsigned int part;
747256fb0e3SMichael Baum 
748256fb0e3SMichael Baum 	part = MLX5_TXOFF_CONFIG(INLINE) ?
749256fb0e3SMichael Baum 	       0 : loc->pkts_sent - loc->pkts_copy;
750256fb0e3SMichael Baum 	head += part;
751256fb0e3SMichael Baum 	if ((uint16_t)(head - txq->elts_comp) >= MLX5_TX_COMP_THRESH ||
752256fb0e3SMichael Baum 	     (MLX5_TXOFF_CONFIG(INLINE) &&
753256fb0e3SMichael Baum 	     (uint16_t)(txq->wqe_ci - txq->wqe_comp) >= txq->wqe_thres)) {
754256fb0e3SMichael Baum 		volatile struct mlx5_wqe *last = loc->wqe_last;
755256fb0e3SMichael Baum 
756256fb0e3SMichael Baum 		MLX5_ASSERT(last);
757256fb0e3SMichael Baum 		txq->elts_comp = head;
758256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(INLINE))
759256fb0e3SMichael Baum 			txq->wqe_comp = txq->wqe_ci;
760256fb0e3SMichael Baum 		/* Request unconditional completion on last WQE. */
761256fb0e3SMichael Baum 		last->cseg.flags = RTE_BE32(MLX5_COMP_ALWAYS <<
762256fb0e3SMichael Baum 					    MLX5_COMP_MODE_OFFSET);
763256fb0e3SMichael Baum 		/* Save elts_head in dedicated free on completion queue. */
764256fb0e3SMichael Baum #ifdef RTE_LIBRTE_MLX5_DEBUG
765256fb0e3SMichael Baum 		txq->fcqs[txq->cq_pi++ & txq->cqe_m] = head |
766256fb0e3SMichael Baum 			  (last->cseg.opcode >> 8) << 16;
767256fb0e3SMichael Baum #else
768256fb0e3SMichael Baum 		txq->fcqs[txq->cq_pi++ & txq->cqe_m] = head;
769256fb0e3SMichael Baum #endif
770256fb0e3SMichael Baum 		/* A CQE slot must always be available. */
771256fb0e3SMichael Baum 		MLX5_ASSERT((txq->cq_pi - txq->cq_ci) <= txq->cqe_s);
772256fb0e3SMichael Baum 	}
773256fb0e3SMichael Baum }
774256fb0e3SMichael Baum 
775256fb0e3SMichael Baum /**
7768fa8d147SViacheslav Ovsiienko  * Set completion request flag for all issued WQEs.
7778fa8d147SViacheslav Ovsiienko  * This routine is intended to be used with enabled fast path tracing
7788fa8d147SViacheslav Ovsiienko  * and send scheduling on time to provide the detailed report in trace
7798fa8d147SViacheslav Ovsiienko  * for send completions on every WQE.
7808fa8d147SViacheslav Ovsiienko  *
7818fa8d147SViacheslav Ovsiienko  * @param txq
7828fa8d147SViacheslav Ovsiienko  *   Pointer to TX queue structure.
7838fa8d147SViacheslav Ovsiienko  * @param loc
7848fa8d147SViacheslav Ovsiienko  *   Pointer to burst routine local context.
7858fa8d147SViacheslav Ovsiienko  * @param olx
7868fa8d147SViacheslav Ovsiienko  *   Configured Tx offloads mask. It is fully defined at
7878fa8d147SViacheslav Ovsiienko  *   compile time and may be used for optimization.
7888fa8d147SViacheslav Ovsiienko  */
7898fa8d147SViacheslav Ovsiienko static __rte_always_inline void
7908fa8d147SViacheslav Ovsiienko mlx5_tx_request_completion_trace(struct mlx5_txq_data *__rte_restrict txq,
7918fa8d147SViacheslav Ovsiienko 				 struct mlx5_txq_local *__rte_restrict loc,
7928fa8d147SViacheslav Ovsiienko 				 unsigned int olx)
7938fa8d147SViacheslav Ovsiienko {
7948fa8d147SViacheslav Ovsiienko 	uint16_t head = txq->elts_comp;
7958fa8d147SViacheslav Ovsiienko 
7968fa8d147SViacheslav Ovsiienko 	while (txq->wqe_comp != txq->wqe_ci) {
7978fa8d147SViacheslav Ovsiienko 		volatile struct mlx5_wqe *wqe;
7988fa8d147SViacheslav Ovsiienko 		uint32_t wqe_n;
7998fa8d147SViacheslav Ovsiienko 
8008fa8d147SViacheslav Ovsiienko 		MLX5_ASSERT(loc->wqe_last);
8018fa8d147SViacheslav Ovsiienko 		wqe = txq->wqes + (txq->wqe_comp & txq->wqe_m);
8028fa8d147SViacheslav Ovsiienko 		if (wqe == loc->wqe_last) {
8038fa8d147SViacheslav Ovsiienko 			head = txq->elts_head;
8048fa8d147SViacheslav Ovsiienko 			head +=	MLX5_TXOFF_CONFIG(INLINE) ?
8058fa8d147SViacheslav Ovsiienko 				0 : loc->pkts_sent - loc->pkts_copy;
8068fa8d147SViacheslav Ovsiienko 			txq->elts_comp = head;
8078fa8d147SViacheslav Ovsiienko 		}
8088fa8d147SViacheslav Ovsiienko 		/* Completion request flag was set on cseg constructing. */
8098fa8d147SViacheslav Ovsiienko #ifdef RTE_LIBRTE_MLX5_DEBUG
8108fa8d147SViacheslav Ovsiienko 		txq->fcqs[txq->cq_pi++ & txq->cqe_m] = head |
8118fa8d147SViacheslav Ovsiienko 			  (wqe->cseg.opcode >> 8) << 16;
8128fa8d147SViacheslav Ovsiienko #else
8138fa8d147SViacheslav Ovsiienko 		txq->fcqs[txq->cq_pi++ & txq->cqe_m] = head;
8148fa8d147SViacheslav Ovsiienko #endif
8158fa8d147SViacheslav Ovsiienko 		/* A CQE slot must always be available. */
8168fa8d147SViacheslav Ovsiienko 		MLX5_ASSERT((txq->cq_pi - txq->cq_ci) <= txq->cqe_s);
8178fa8d147SViacheslav Ovsiienko 		/* Advance to the next WQE in the queue. */
8188fa8d147SViacheslav Ovsiienko 		wqe_n = rte_be_to_cpu_32(wqe->cseg.sq_ds) & 0x3F;
8198fa8d147SViacheslav Ovsiienko 		txq->wqe_comp += RTE_ALIGN(wqe_n, 4) / 4;
8208fa8d147SViacheslav Ovsiienko 	}
8218fa8d147SViacheslav Ovsiienko }
8228fa8d147SViacheslav Ovsiienko 
8238fa8d147SViacheslav Ovsiienko /**
824256fb0e3SMichael Baum  * Build the Control Segment with specified opcode:
825256fb0e3SMichael Baum  * - MLX5_OPCODE_SEND
826256fb0e3SMichael Baum  * - MLX5_OPCODE_ENHANCED_MPSW
827256fb0e3SMichael Baum  * - MLX5_OPCODE_TSO
828256fb0e3SMichael Baum  *
829256fb0e3SMichael Baum  * @param txq
830256fb0e3SMichael Baum  *   Pointer to TX queue structure.
831256fb0e3SMichael Baum  * @param loc
832256fb0e3SMichael Baum  *   Pointer to burst routine local context.
833256fb0e3SMichael Baum  * @param wqe
834256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Control Segment.
835256fb0e3SMichael Baum  * @param ds
836256fb0e3SMichael Baum  *   Supposed length of WQE in segments.
837256fb0e3SMichael Baum  * @param opcode
838256fb0e3SMichael Baum  *   SQ WQE opcode to put into Control Segment.
839256fb0e3SMichael Baum  * @param olx
840256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
841256fb0e3SMichael Baum  *   compile time and may be used for optimization.
842256fb0e3SMichael Baum  */
843256fb0e3SMichael Baum static __rte_always_inline void
844256fb0e3SMichael Baum mlx5_tx_cseg_init(struct mlx5_txq_data *__rte_restrict txq,
845256fb0e3SMichael Baum 		  struct mlx5_txq_local *__rte_restrict loc __rte_unused,
846256fb0e3SMichael Baum 		  struct mlx5_wqe *__rte_restrict wqe,
847256fb0e3SMichael Baum 		  unsigned int ds,
848256fb0e3SMichael Baum 		  unsigned int opcode,
8498fa8d147SViacheslav Ovsiienko 		  unsigned int olx)
850256fb0e3SMichael Baum {
851256fb0e3SMichael Baum 	struct mlx5_wqe_cseg *__rte_restrict cs = &wqe->cseg;
852*02932480STim Martin 	uint64_t real_time;
853256fb0e3SMichael Baum 
854256fb0e3SMichael Baum 	/* For legacy MPW replace the EMPW by TSO with modifier. */
855256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(MPW) && opcode == MLX5_OPCODE_ENHANCED_MPSW)
856256fb0e3SMichael Baum 		opcode = MLX5_OPCODE_TSO | MLX5_OPC_MOD_MPW << 24;
857256fb0e3SMichael Baum 	cs->opcode = rte_cpu_to_be_32((txq->wqe_ci << 8) | opcode);
858256fb0e3SMichael Baum 	cs->sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | ds);
8598fa8d147SViacheslav Ovsiienko 	if (MLX5_TXOFF_CONFIG(TXPP) && __rte_trace_point_fp_is_enabled())
8608fa8d147SViacheslav Ovsiienko 		cs->flags = RTE_BE32(MLX5_COMP_ALWAYS <<
8618fa8d147SViacheslav Ovsiienko 				     MLX5_COMP_MODE_OFFSET);
8628fa8d147SViacheslav Ovsiienko 	else
863256fb0e3SMichael Baum 		cs->flags = RTE_BE32(MLX5_COMP_ONLY_FIRST_ERR <<
864256fb0e3SMichael Baum 				     MLX5_COMP_MODE_OFFSET);
865256fb0e3SMichael Baum 	cs->misc = RTE_BE32(0);
866*02932480STim Martin 	if (__rte_trace_point_fp_is_enabled()) {
867*02932480STim Martin 		real_time = mlx5_read_pcibar_clock_from_txq(txq);
868*02932480STim Martin 		if (!loc->pkts_sent)
869*02932480STim Martin 			rte_pmd_mlx5_trace_tx_entry(real_time, txq->port_id, txq->idx);
870*02932480STim Martin 		rte_pmd_mlx5_trace_tx_wqe(real_time, (txq->wqe_ci << 8) | opcode);
871*02932480STim Martin 	}
872256fb0e3SMichael Baum }
873256fb0e3SMichael Baum 
874256fb0e3SMichael Baum /**
875256fb0e3SMichael Baum  * Build the Synchronize Queue Segment with specified completion index.
876256fb0e3SMichael Baum  *
877256fb0e3SMichael Baum  * @param txq
878256fb0e3SMichael Baum  *   Pointer to TX queue structure.
879256fb0e3SMichael Baum  * @param loc
880256fb0e3SMichael Baum  *   Pointer to burst routine local context.
881256fb0e3SMichael Baum  * @param wqe
882256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Control Segment.
883256fb0e3SMichael Baum  * @param wci
884256fb0e3SMichael Baum  *   Completion index in Clock Queue to wait.
885256fb0e3SMichael Baum  * @param olx
886256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
887256fb0e3SMichael Baum  *   compile time and may be used for optimization.
888256fb0e3SMichael Baum  */
889256fb0e3SMichael Baum static __rte_always_inline void
89049e87976SViacheslav Ovsiienko mlx5_tx_qseg_init(struct mlx5_txq_data *restrict txq,
891256fb0e3SMichael Baum 		  struct mlx5_txq_local *restrict loc __rte_unused,
892256fb0e3SMichael Baum 		  struct mlx5_wqe *restrict wqe,
893256fb0e3SMichael Baum 		  unsigned int wci,
894256fb0e3SMichael Baum 		  unsigned int olx __rte_unused)
895256fb0e3SMichael Baum {
896256fb0e3SMichael Baum 	struct mlx5_wqe_qseg *qs;
897256fb0e3SMichael Baum 
898256fb0e3SMichael Baum 	qs = RTE_PTR_ADD(wqe, MLX5_WSEG_SIZE);
899256fb0e3SMichael Baum 	qs->max_index = rte_cpu_to_be_32(wci);
900256fb0e3SMichael Baum 	qs->qpn_cqn = rte_cpu_to_be_32(txq->sh->txpp.clock_queue.cq_obj.cq->id);
901256fb0e3SMichael Baum 	qs->reserved0 = RTE_BE32(0);
902256fb0e3SMichael Baum 	qs->reserved1 = RTE_BE32(0);
903256fb0e3SMichael Baum }
904256fb0e3SMichael Baum 
905256fb0e3SMichael Baum /**
90649e87976SViacheslav Ovsiienko  * Build the Wait on Time Segment with specified timestamp value.
90749e87976SViacheslav Ovsiienko  *
90849e87976SViacheslav Ovsiienko  * @param txq
90949e87976SViacheslav Ovsiienko  *   Pointer to TX queue structure.
91049e87976SViacheslav Ovsiienko  * @param loc
91149e87976SViacheslav Ovsiienko  *   Pointer to burst routine local context.
91249e87976SViacheslav Ovsiienko  * @param wqe
91349e87976SViacheslav Ovsiienko  *   Pointer to WQE to fill with built Control Segment.
91449e87976SViacheslav Ovsiienko  * @param ts
91549e87976SViacheslav Ovsiienko  *   Timesatmp value to wait.
91649e87976SViacheslav Ovsiienko  * @param olx
91749e87976SViacheslav Ovsiienko  *   Configured Tx offloads mask. It is fully defined at
91849e87976SViacheslav Ovsiienko  *   compile time and may be used for optimization.
91949e87976SViacheslav Ovsiienko  */
92049e87976SViacheslav Ovsiienko static __rte_always_inline void
92149e87976SViacheslav Ovsiienko mlx5_tx_wseg_init(struct mlx5_txq_data *restrict txq,
92249e87976SViacheslav Ovsiienko 		  struct mlx5_txq_local *restrict loc __rte_unused,
92349e87976SViacheslav Ovsiienko 		  struct mlx5_wqe *restrict wqe,
92449e87976SViacheslav Ovsiienko 		  uint64_t ts,
92549e87976SViacheslav Ovsiienko 		  unsigned int olx __rte_unused)
92649e87976SViacheslav Ovsiienko {
92749e87976SViacheslav Ovsiienko 	struct mlx5_wqe_wseg *ws;
92849e87976SViacheslav Ovsiienko 
92949e87976SViacheslav Ovsiienko 	ws = RTE_PTR_ADD(wqe, MLX5_WSEG_SIZE);
9302c7cb21aSViacheslav Ovsiienko 	ws->operation = rte_cpu_to_be_32(MLX5_WAIT_COND_CYCLIC_SMALLER);
93149e87976SViacheslav Ovsiienko 	ws->lkey = RTE_BE32(0);
93249e87976SViacheslav Ovsiienko 	ws->va_high = RTE_BE32(0);
93349e87976SViacheslav Ovsiienko 	ws->va_low = RTE_BE32(0);
93449e87976SViacheslav Ovsiienko 	if (txq->rt_timestamp) {
93549e87976SViacheslav Ovsiienko 		ts = ts % (uint64_t)NS_PER_S
93649e87976SViacheslav Ovsiienko 		   | (ts / (uint64_t)NS_PER_S) << 32;
93749e87976SViacheslav Ovsiienko 	}
93849e87976SViacheslav Ovsiienko 	ws->value = rte_cpu_to_be_64(ts);
93949e87976SViacheslav Ovsiienko 	ws->mask = txq->rt_timemask;
94049e87976SViacheslav Ovsiienko }
94149e87976SViacheslav Ovsiienko 
94249e87976SViacheslav Ovsiienko /**
943256fb0e3SMichael Baum  * Build the Ethernet Segment without inlined data.
944256fb0e3SMichael Baum  * Supports Software Parser, Checksums and VLAN insertion Tx offload features.
945256fb0e3SMichael Baum  *
946256fb0e3SMichael Baum  * @param txq
947256fb0e3SMichael Baum  *   Pointer to TX queue structure.
948256fb0e3SMichael Baum  * @param loc
949256fb0e3SMichael Baum  *   Pointer to burst routine local context.
950256fb0e3SMichael Baum  * @param wqe
951256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Ethernet Segment.
952256fb0e3SMichael Baum  * @param olx
953256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
954256fb0e3SMichael Baum  *   compile time and may be used for optimization.
955256fb0e3SMichael Baum  */
956256fb0e3SMichael Baum static __rte_always_inline void
957256fb0e3SMichael Baum mlx5_tx_eseg_none(struct mlx5_txq_data *__rte_restrict txq __rte_unused,
958256fb0e3SMichael Baum 		  struct mlx5_txq_local *__rte_restrict loc,
959256fb0e3SMichael Baum 		  struct mlx5_wqe *__rte_restrict wqe,
960256fb0e3SMichael Baum 		  unsigned int olx)
961256fb0e3SMichael Baum {
962256fb0e3SMichael Baum 	struct mlx5_wqe_eseg *__rte_restrict es = &wqe->eseg;
963256fb0e3SMichael Baum 	uint32_t csum;
964256fb0e3SMichael Baum 
965256fb0e3SMichael Baum 	/*
966256fb0e3SMichael Baum 	 * Calculate and set check sum flags first, dword field
967256fb0e3SMichael Baum 	 * in segment may be shared with Software Parser flags.
968256fb0e3SMichael Baum 	 */
969256fb0e3SMichael Baum 	csum = MLX5_TXOFF_CONFIG(CSUM) ? txq_ol_cksum_to_cs(loc->mbuf) : 0;
970256fb0e3SMichael Baum 	es->flags = rte_cpu_to_le_32(csum);
971256fb0e3SMichael Baum 	/*
972256fb0e3SMichael Baum 	 * Calculate and set Software Parser offsets and flags.
973256fb0e3SMichael Baum 	 * These flags a set for custom UDP and IP tunnel packets.
974256fb0e3SMichael Baum 	 */
975256fb0e3SMichael Baum 	es->swp_offs = txq_mbuf_to_swp(loc, &es->swp_flags, olx);
976256fb0e3SMichael Baum 	/* Fill metadata field if needed. */
977256fb0e3SMichael Baum 	es->metadata = MLX5_TXOFF_CONFIG(METADATA) ?
978daa02b5cSOlivier Matz 		       loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
9796728fe93SBing Zhao 		       rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) :
9806728fe93SBing Zhao 		       0 : 0;
981256fb0e3SMichael Baum 	/* Engage VLAN tag insertion feature if requested. */
982256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(VLAN) &&
983daa02b5cSOlivier Matz 	    loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) {
984256fb0e3SMichael Baum 		/*
985256fb0e3SMichael Baum 		 * We should get here only if device support
986256fb0e3SMichael Baum 		 * this feature correctly.
987256fb0e3SMichael Baum 		 */
988256fb0e3SMichael Baum 		MLX5_ASSERT(txq->vlan_en);
989256fb0e3SMichael Baum 		es->inline_hdr = rte_cpu_to_be_32(MLX5_ETH_WQE_VLAN_INSERT |
990256fb0e3SMichael Baum 						  loc->mbuf->vlan_tci);
991256fb0e3SMichael Baum 	} else {
992256fb0e3SMichael Baum 		es->inline_hdr = RTE_BE32(0);
993256fb0e3SMichael Baum 	}
994256fb0e3SMichael Baum }
995256fb0e3SMichael Baum 
996256fb0e3SMichael Baum /**
997256fb0e3SMichael Baum  * Build the Ethernet Segment with minimal inlined data
998256fb0e3SMichael Baum  * of MLX5_ESEG_MIN_INLINE_SIZE bytes length. This is
999256fb0e3SMichael Baum  * used to fill the gap in single WQEBB WQEs.
1000256fb0e3SMichael Baum  * Supports Software Parser, Checksums and VLAN
1001256fb0e3SMichael Baum  * insertion Tx offload features.
1002256fb0e3SMichael Baum  *
1003256fb0e3SMichael Baum  * @param txq
1004256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1005256fb0e3SMichael Baum  * @param loc
1006256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1007256fb0e3SMichael Baum  * @param wqe
1008256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Ethernet Segment.
1009256fb0e3SMichael Baum  * @param vlan
1010256fb0e3SMichael Baum  *   Length of VLAN tag insertion if any.
1011256fb0e3SMichael Baum  * @param olx
1012256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1013256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1014256fb0e3SMichael Baum  */
1015256fb0e3SMichael Baum static __rte_always_inline void
1016256fb0e3SMichael Baum mlx5_tx_eseg_dmin(struct mlx5_txq_data *__rte_restrict txq __rte_unused,
1017256fb0e3SMichael Baum 		  struct mlx5_txq_local *__rte_restrict loc,
1018256fb0e3SMichael Baum 		  struct mlx5_wqe *__rte_restrict wqe,
1019256fb0e3SMichael Baum 		  unsigned int vlan,
1020256fb0e3SMichael Baum 		  unsigned int olx)
1021256fb0e3SMichael Baum {
1022256fb0e3SMichael Baum 	struct mlx5_wqe_eseg *__rte_restrict es = &wqe->eseg;
1023256fb0e3SMichael Baum 	uint32_t csum;
1024256fb0e3SMichael Baum 	uint8_t *psrc, *pdst;
1025256fb0e3SMichael Baum 
1026256fb0e3SMichael Baum 	/*
1027256fb0e3SMichael Baum 	 * Calculate and set check sum flags first, dword field
1028256fb0e3SMichael Baum 	 * in segment may be shared with Software Parser flags.
1029256fb0e3SMichael Baum 	 */
1030256fb0e3SMichael Baum 	csum = MLX5_TXOFF_CONFIG(CSUM) ? txq_ol_cksum_to_cs(loc->mbuf) : 0;
1031256fb0e3SMichael Baum 	es->flags = rte_cpu_to_le_32(csum);
1032256fb0e3SMichael Baum 	/*
1033256fb0e3SMichael Baum 	 * Calculate and set Software Parser offsets and flags.
1034256fb0e3SMichael Baum 	 * These flags a set for custom UDP and IP tunnel packets.
1035256fb0e3SMichael Baum 	 */
1036256fb0e3SMichael Baum 	es->swp_offs = txq_mbuf_to_swp(loc, &es->swp_flags, olx);
1037256fb0e3SMichael Baum 	/* Fill metadata field if needed. */
1038256fb0e3SMichael Baum 	es->metadata = MLX5_TXOFF_CONFIG(METADATA) ?
1039daa02b5cSOlivier Matz 		       loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
10406728fe93SBing Zhao 		       rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) :
10416728fe93SBing Zhao 		       0 : 0;
1042256fb0e3SMichael Baum 	psrc = rte_pktmbuf_mtod(loc->mbuf, uint8_t *);
1043256fb0e3SMichael Baum 	es->inline_hdr_sz = RTE_BE16(MLX5_ESEG_MIN_INLINE_SIZE);
1044256fb0e3SMichael Baum 	es->inline_data = *(unaligned_uint16_t *)psrc;
1045256fb0e3SMichael Baum 	psrc +=	sizeof(uint16_t);
1046256fb0e3SMichael Baum 	pdst = (uint8_t *)(es + 1);
1047256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(VLAN) && vlan) {
1048256fb0e3SMichael Baum 		/* Implement VLAN tag insertion as part inline data. */
1049256fb0e3SMichael Baum 		memcpy(pdst, psrc, 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t));
1050256fb0e3SMichael Baum 		pdst += 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t);
1051256fb0e3SMichael Baum 		psrc +=	2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t);
1052256fb0e3SMichael Baum 		/* Insert VLAN ethertype + VLAN tag. */
1053256fb0e3SMichael Baum 		*(unaligned_uint32_t *)pdst = rte_cpu_to_be_32
1054256fb0e3SMichael Baum 						((RTE_ETHER_TYPE_VLAN << 16) |
1055256fb0e3SMichael Baum 						 loc->mbuf->vlan_tci);
1056256fb0e3SMichael Baum 		pdst += sizeof(struct rte_vlan_hdr);
1057256fb0e3SMichael Baum 		/* Copy the rest two bytes from packet data. */
1058256fb0e3SMichael Baum 		MLX5_ASSERT(pdst == RTE_PTR_ALIGN(pdst, sizeof(uint16_t)));
1059256fb0e3SMichael Baum 		*(uint16_t *)pdst = *(unaligned_uint16_t *)psrc;
1060256fb0e3SMichael Baum 	} else {
1061256fb0e3SMichael Baum 		/* Fill the gap in the title WQEBB with inline data. */
1062256fb0e3SMichael Baum 		rte_mov16(pdst, psrc);
1063256fb0e3SMichael Baum 	}
1064256fb0e3SMichael Baum }
1065256fb0e3SMichael Baum 
1066256fb0e3SMichael Baum /**
1067256fb0e3SMichael Baum  * Build the Ethernet Segment with entire packet data inlining. Checks the
1068256fb0e3SMichael Baum  * boundary of WQEBB and ring buffer wrapping, supports Software Parser,
1069256fb0e3SMichael Baum  * Checksums and VLAN insertion Tx offload features.
1070256fb0e3SMichael Baum  *
1071256fb0e3SMichael Baum  * @param txq
1072256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1073256fb0e3SMichael Baum  * @param loc
1074256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1075256fb0e3SMichael Baum  * @param wqe
1076256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Ethernet Segment.
1077256fb0e3SMichael Baum  * @param vlan
1078256fb0e3SMichael Baum  *   Length of VLAN tag insertion if any.
1079256fb0e3SMichael Baum  * @param inlen
1080256fb0e3SMichael Baum  *   Length of data to inline (VLAN included, if any).
1081256fb0e3SMichael Baum  * @param tso
1082256fb0e3SMichael Baum  *   TSO flag, set mss field from the packet.
1083256fb0e3SMichael Baum  * @param olx
1084256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1085256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1086256fb0e3SMichael Baum  *
1087256fb0e3SMichael Baum  * @return
1088256fb0e3SMichael Baum  *   Pointer to the next Data Segment (aligned and wrapped around).
1089256fb0e3SMichael Baum  */
1090256fb0e3SMichael Baum static __rte_always_inline struct mlx5_wqe_dseg *
1091256fb0e3SMichael Baum mlx5_tx_eseg_data(struct mlx5_txq_data *__rte_restrict txq,
1092256fb0e3SMichael Baum 		  struct mlx5_txq_local *__rte_restrict loc,
1093256fb0e3SMichael Baum 		  struct mlx5_wqe *__rte_restrict wqe,
1094256fb0e3SMichael Baum 		  unsigned int vlan,
1095256fb0e3SMichael Baum 		  unsigned int inlen,
1096256fb0e3SMichael Baum 		  unsigned int tso,
1097256fb0e3SMichael Baum 		  unsigned int olx)
1098256fb0e3SMichael Baum {
1099256fb0e3SMichael Baum 	struct mlx5_wqe_eseg *__rte_restrict es = &wqe->eseg;
1100256fb0e3SMichael Baum 	uint32_t csum;
1101256fb0e3SMichael Baum 	uint8_t *psrc, *pdst;
1102256fb0e3SMichael Baum 	unsigned int part;
1103256fb0e3SMichael Baum 
1104256fb0e3SMichael Baum 	/*
1105256fb0e3SMichael Baum 	 * Calculate and set check sum flags first, dword field
1106256fb0e3SMichael Baum 	 * in segment may be shared with Software Parser flags.
1107256fb0e3SMichael Baum 	 */
1108256fb0e3SMichael Baum 	csum = MLX5_TXOFF_CONFIG(CSUM) ? txq_ol_cksum_to_cs(loc->mbuf) : 0;
1109256fb0e3SMichael Baum 	if (tso) {
1110256fb0e3SMichael Baum 		csum <<= 24;
1111256fb0e3SMichael Baum 		csum |= loc->mbuf->tso_segsz;
1112256fb0e3SMichael Baum 		es->flags = rte_cpu_to_be_32(csum);
1113256fb0e3SMichael Baum 	} else {
1114256fb0e3SMichael Baum 		es->flags = rte_cpu_to_le_32(csum);
1115256fb0e3SMichael Baum 	}
1116256fb0e3SMichael Baum 	/*
1117256fb0e3SMichael Baum 	 * Calculate and set Software Parser offsets and flags.
1118256fb0e3SMichael Baum 	 * These flags a set for custom UDP and IP tunnel packets.
1119256fb0e3SMichael Baum 	 */
1120256fb0e3SMichael Baum 	es->swp_offs = txq_mbuf_to_swp(loc, &es->swp_flags, olx);
1121256fb0e3SMichael Baum 	/* Fill metadata field if needed. */
1122256fb0e3SMichael Baum 	es->metadata = MLX5_TXOFF_CONFIG(METADATA) ?
1123daa02b5cSOlivier Matz 		       loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
11246728fe93SBing Zhao 		       rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) :
11256728fe93SBing Zhao 		       0 : 0;
1126256fb0e3SMichael Baum 	psrc = rte_pktmbuf_mtod(loc->mbuf, uint8_t *);
1127256fb0e3SMichael Baum 	es->inline_hdr_sz = rte_cpu_to_be_16(inlen);
1128256fb0e3SMichael Baum 	es->inline_data = *(unaligned_uint16_t *)psrc;
1129256fb0e3SMichael Baum 	psrc +=	sizeof(uint16_t);
1130256fb0e3SMichael Baum 	pdst = (uint8_t *)(es + 1);
1131256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(VLAN) && vlan) {
1132256fb0e3SMichael Baum 		/* Implement VLAN tag insertion as part inline data. */
1133256fb0e3SMichael Baum 		memcpy(pdst, psrc, 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t));
1134256fb0e3SMichael Baum 		pdst += 2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t);
1135256fb0e3SMichael Baum 		psrc +=	2 * RTE_ETHER_ADDR_LEN - sizeof(uint16_t);
1136256fb0e3SMichael Baum 		/* Insert VLAN ethertype + VLAN tag. */
1137256fb0e3SMichael Baum 		*(unaligned_uint32_t *)pdst = rte_cpu_to_be_32
1138256fb0e3SMichael Baum 						((RTE_ETHER_TYPE_VLAN << 16) |
1139256fb0e3SMichael Baum 						 loc->mbuf->vlan_tci);
1140256fb0e3SMichael Baum 		pdst += sizeof(struct rte_vlan_hdr);
1141256fb0e3SMichael Baum 		/* Copy the rest two bytes from packet data. */
1142256fb0e3SMichael Baum 		MLX5_ASSERT(pdst == RTE_PTR_ALIGN(pdst, sizeof(uint16_t)));
1143256fb0e3SMichael Baum 		*(uint16_t *)pdst = *(unaligned_uint16_t *)psrc;
1144256fb0e3SMichael Baum 		psrc += sizeof(uint16_t);
1145256fb0e3SMichael Baum 	} else {
1146256fb0e3SMichael Baum 		/* Fill the gap in the title WQEBB with inline data. */
1147256fb0e3SMichael Baum 		rte_mov16(pdst, psrc);
1148256fb0e3SMichael Baum 		psrc += sizeof(rte_v128u32_t);
1149256fb0e3SMichael Baum 	}
1150256fb0e3SMichael Baum 	pdst = (uint8_t *)(es + 2);
1151256fb0e3SMichael Baum 	MLX5_ASSERT(inlen >= MLX5_ESEG_MIN_INLINE_SIZE);
1152256fb0e3SMichael Baum 	MLX5_ASSERT(pdst < (uint8_t *)txq->wqes_end);
1153256fb0e3SMichael Baum 	inlen -= MLX5_ESEG_MIN_INLINE_SIZE;
1154256fb0e3SMichael Baum 	if (!inlen) {
1155256fb0e3SMichael Baum 		MLX5_ASSERT(pdst == RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE));
1156256fb0e3SMichael Baum 		return (struct mlx5_wqe_dseg *)pdst;
1157256fb0e3SMichael Baum 	}
1158256fb0e3SMichael Baum 	/*
1159256fb0e3SMichael Baum 	 * The WQEBB space availability is checked by caller.
1160256fb0e3SMichael Baum 	 * Here we should be aware of WQE ring buffer wraparound only.
1161256fb0e3SMichael Baum 	 */
1162256fb0e3SMichael Baum 	part = (uint8_t *)txq->wqes_end - pdst;
1163256fb0e3SMichael Baum 	part = RTE_MIN(part, inlen);
1164256fb0e3SMichael Baum 	do {
1165256fb0e3SMichael Baum 		rte_memcpy(pdst, psrc, part);
1166256fb0e3SMichael Baum 		inlen -= part;
1167256fb0e3SMichael Baum 		if (likely(!inlen)) {
1168256fb0e3SMichael Baum 			/*
1169256fb0e3SMichael Baum 			 * If return value is not used by the caller
1170256fb0e3SMichael Baum 			 * the code below will be optimized out.
1171256fb0e3SMichael Baum 			 */
1172256fb0e3SMichael Baum 			pdst += part;
1173256fb0e3SMichael Baum 			pdst = RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE);
1174256fb0e3SMichael Baum 			if (unlikely(pdst >= (uint8_t *)txq->wqes_end))
1175256fb0e3SMichael Baum 				pdst = (uint8_t *)txq->wqes;
1176256fb0e3SMichael Baum 			return (struct mlx5_wqe_dseg *)pdst;
1177256fb0e3SMichael Baum 		}
1178256fb0e3SMichael Baum 		pdst = (uint8_t *)txq->wqes;
1179256fb0e3SMichael Baum 		psrc += part;
1180256fb0e3SMichael Baum 		part = inlen;
1181256fb0e3SMichael Baum 	} while (true);
1182256fb0e3SMichael Baum }
1183256fb0e3SMichael Baum 
1184256fb0e3SMichael Baum /**
1185256fb0e3SMichael Baum  * Copy data from chain of mbuf to the specified linear buffer.
1186256fb0e3SMichael Baum  * Checksums and VLAN insertion Tx offload features. If data
1187256fb0e3SMichael Baum  * from some mbuf copied completely this mbuf is freed. Local
1188256fb0e3SMichael Baum  * structure is used to keep the byte stream state.
1189256fb0e3SMichael Baum  *
1190256fb0e3SMichael Baum  * @param pdst
1191256fb0e3SMichael Baum  *   Pointer to the destination linear buffer.
1192256fb0e3SMichael Baum  * @param loc
1193256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1194256fb0e3SMichael Baum  * @param len
1195256fb0e3SMichael Baum  *   Length of data to be copied.
1196256fb0e3SMichael Baum  * @param must
1197256fb0e3SMichael Baum  *   Length of data to be copied ignoring no inline hint.
1198256fb0e3SMichael Baum  * @param olx
1199256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1200256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1201256fb0e3SMichael Baum  *
1202256fb0e3SMichael Baum  * @return
1203256fb0e3SMichael Baum  *   Number of actual copied data bytes. This is always greater than or
1204256fb0e3SMichael Baum  *   equal to must parameter and might be lesser than len in no inline
1205256fb0e3SMichael Baum  *   hint flag is encountered.
1206256fb0e3SMichael Baum  */
1207256fb0e3SMichael Baum static __rte_always_inline unsigned int
1208256fb0e3SMichael Baum mlx5_tx_mseg_memcpy(uint8_t *pdst,
1209256fb0e3SMichael Baum 		    struct mlx5_txq_local *__rte_restrict loc,
1210256fb0e3SMichael Baum 		    unsigned int len,
1211256fb0e3SMichael Baum 		    unsigned int must,
1212256fb0e3SMichael Baum 		    unsigned int olx __rte_unused)
1213256fb0e3SMichael Baum {
1214256fb0e3SMichael Baum 	struct rte_mbuf *mbuf;
1215256fb0e3SMichael Baum 	unsigned int part, dlen, copy = 0;
1216256fb0e3SMichael Baum 	uint8_t *psrc;
1217256fb0e3SMichael Baum 
1218256fb0e3SMichael Baum 	MLX5_ASSERT(len);
1219256fb0e3SMichael Baum 	do {
1220256fb0e3SMichael Baum 		/* Allow zero length packets, must check first. */
1221256fb0e3SMichael Baum 		dlen = rte_pktmbuf_data_len(loc->mbuf);
1222256fb0e3SMichael Baum 		if (dlen <= loc->mbuf_off) {
1223256fb0e3SMichael Baum 			/* Exhausted packet, just free. */
1224256fb0e3SMichael Baum 			mbuf = loc->mbuf;
1225256fb0e3SMichael Baum 			loc->mbuf = mbuf->next;
1226256fb0e3SMichael Baum 			rte_pktmbuf_free_seg(mbuf);
1227256fb0e3SMichael Baum 			loc->mbuf_off = 0;
1228256fb0e3SMichael Baum 			MLX5_ASSERT(loc->mbuf_nseg > 1);
1229256fb0e3SMichael Baum 			MLX5_ASSERT(loc->mbuf);
1230256fb0e3SMichael Baum 			--loc->mbuf_nseg;
1231daa02b5cSOlivier Matz 			if (loc->mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE) {
1232256fb0e3SMichael Baum 				unsigned int diff;
1233256fb0e3SMichael Baum 
1234256fb0e3SMichael Baum 				if (copy >= must) {
1235256fb0e3SMichael Baum 					/*
1236256fb0e3SMichael Baum 					 * We already copied the minimal
1237256fb0e3SMichael Baum 					 * requested amount of data.
1238256fb0e3SMichael Baum 					 */
1239256fb0e3SMichael Baum 					return copy;
1240256fb0e3SMichael Baum 				}
1241256fb0e3SMichael Baum 				diff = must - copy;
1242256fb0e3SMichael Baum 				if (diff <= rte_pktmbuf_data_len(loc->mbuf)) {
1243256fb0e3SMichael Baum 					/*
1244256fb0e3SMichael Baum 					 * Copy only the minimal required
1245fa488345SDariusz Sosnowski 					 * part of the data buffer. Limit amount
1246fa488345SDariusz Sosnowski 					 * of data to be copied to the length of
1247fa488345SDariusz Sosnowski 					 * available space.
1248256fb0e3SMichael Baum 					 */
1249fa488345SDariusz Sosnowski 					len = RTE_MIN(len, diff);
1250256fb0e3SMichael Baum 				}
1251256fb0e3SMichael Baum 			}
1252256fb0e3SMichael Baum 			continue;
1253256fb0e3SMichael Baum 		}
1254256fb0e3SMichael Baum 		dlen -= loc->mbuf_off;
1255256fb0e3SMichael Baum 		psrc = rte_pktmbuf_mtod_offset(loc->mbuf, uint8_t *,
1256256fb0e3SMichael Baum 					       loc->mbuf_off);
1257256fb0e3SMichael Baum 		part = RTE_MIN(len, dlen);
1258256fb0e3SMichael Baum 		rte_memcpy(pdst, psrc, part);
1259256fb0e3SMichael Baum 		copy += part;
1260256fb0e3SMichael Baum 		loc->mbuf_off += part;
1261256fb0e3SMichael Baum 		len -= part;
1262256fb0e3SMichael Baum 		if (!len) {
1263256fb0e3SMichael Baum 			if (loc->mbuf_off >= rte_pktmbuf_data_len(loc->mbuf)) {
1264256fb0e3SMichael Baum 				loc->mbuf_off = 0;
1265256fb0e3SMichael Baum 				/* Exhausted packet, just free. */
1266256fb0e3SMichael Baum 				mbuf = loc->mbuf;
1267256fb0e3SMichael Baum 				loc->mbuf = mbuf->next;
1268256fb0e3SMichael Baum 				rte_pktmbuf_free_seg(mbuf);
1269256fb0e3SMichael Baum 				loc->mbuf_off = 0;
1270256fb0e3SMichael Baum 				MLX5_ASSERT(loc->mbuf_nseg >= 1);
1271256fb0e3SMichael Baum 				--loc->mbuf_nseg;
1272256fb0e3SMichael Baum 			}
1273256fb0e3SMichael Baum 			return copy;
1274256fb0e3SMichael Baum 		}
1275256fb0e3SMichael Baum 		pdst += part;
1276256fb0e3SMichael Baum 	} while (true);
1277256fb0e3SMichael Baum }
1278256fb0e3SMichael Baum 
1279256fb0e3SMichael Baum /**
1280256fb0e3SMichael Baum  * Build the Ethernet Segment with inlined data from multi-segment packet.
1281256fb0e3SMichael Baum  * Checks the boundary of WQEBB and ring buffer wrapping, supports Software
1282256fb0e3SMichael Baum  * Parser, Checksums and VLAN insertion Tx offload features.
1283256fb0e3SMichael Baum  *
1284256fb0e3SMichael Baum  * @param txq
1285256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1286256fb0e3SMichael Baum  * @param loc
1287256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1288256fb0e3SMichael Baum  * @param wqe
1289256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Ethernet Segment.
1290256fb0e3SMichael Baum  * @param vlan
1291256fb0e3SMichael Baum  *   Length of VLAN tag insertion if any.
1292256fb0e3SMichael Baum  * @param inlen
1293256fb0e3SMichael Baum  *   Length of data to inline (VLAN included, if any).
1294256fb0e3SMichael Baum  * @param tso
1295256fb0e3SMichael Baum  *   TSO flag, set mss field from the packet.
1296256fb0e3SMichael Baum  * @param olx
1297256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1298256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1299256fb0e3SMichael Baum  *
1300256fb0e3SMichael Baum  * @return
1301256fb0e3SMichael Baum  *   Pointer to the next Data Segment (aligned and possible NOT wrapped
1302256fb0e3SMichael Baum  *   around - caller should do wrapping check on its own).
1303256fb0e3SMichael Baum  */
1304256fb0e3SMichael Baum static __rte_always_inline struct mlx5_wqe_dseg *
1305256fb0e3SMichael Baum mlx5_tx_eseg_mdat(struct mlx5_txq_data *__rte_restrict txq,
1306256fb0e3SMichael Baum 		  struct mlx5_txq_local *__rte_restrict loc,
1307256fb0e3SMichael Baum 		  struct mlx5_wqe *__rte_restrict wqe,
1308256fb0e3SMichael Baum 		  unsigned int vlan,
1309256fb0e3SMichael Baum 		  unsigned int inlen,
1310256fb0e3SMichael Baum 		  unsigned int tso,
1311256fb0e3SMichael Baum 		  unsigned int olx)
1312256fb0e3SMichael Baum {
1313256fb0e3SMichael Baum 	struct mlx5_wqe_eseg *__rte_restrict es = &wqe->eseg;
1314256fb0e3SMichael Baum 	uint32_t csum;
1315256fb0e3SMichael Baum 	uint8_t *pdst;
1316256fb0e3SMichael Baum 	unsigned int part, tlen = 0;
1317256fb0e3SMichael Baum 
1318256fb0e3SMichael Baum 	/*
1319256fb0e3SMichael Baum 	 * Calculate and set check sum flags first, uint32_t field
1320256fb0e3SMichael Baum 	 * in segment may be shared with Software Parser flags.
1321256fb0e3SMichael Baum 	 */
1322256fb0e3SMichael Baum 	csum = MLX5_TXOFF_CONFIG(CSUM) ? txq_ol_cksum_to_cs(loc->mbuf) : 0;
1323256fb0e3SMichael Baum 	if (tso) {
1324256fb0e3SMichael Baum 		csum <<= 24;
1325256fb0e3SMichael Baum 		csum |= loc->mbuf->tso_segsz;
1326256fb0e3SMichael Baum 		es->flags = rte_cpu_to_be_32(csum);
1327256fb0e3SMichael Baum 	} else {
1328256fb0e3SMichael Baum 		es->flags = rte_cpu_to_le_32(csum);
1329256fb0e3SMichael Baum 	}
1330256fb0e3SMichael Baum 	/*
1331256fb0e3SMichael Baum 	 * Calculate and set Software Parser offsets and flags.
1332256fb0e3SMichael Baum 	 * These flags a set for custom UDP and IP tunnel packets.
1333256fb0e3SMichael Baum 	 */
1334256fb0e3SMichael Baum 	es->swp_offs = txq_mbuf_to_swp(loc, &es->swp_flags, olx);
1335256fb0e3SMichael Baum 	/* Fill metadata field if needed. */
1336256fb0e3SMichael Baum 	es->metadata = MLX5_TXOFF_CONFIG(METADATA) ?
1337daa02b5cSOlivier Matz 		       loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
13386728fe93SBing Zhao 		       rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) :
13396728fe93SBing Zhao 		       0 : 0;
1340256fb0e3SMichael Baum 	MLX5_ASSERT(inlen >= MLX5_ESEG_MIN_INLINE_SIZE);
1341256fb0e3SMichael Baum 	pdst = (uint8_t *)&es->inline_data;
1342256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(VLAN) && vlan) {
1343256fb0e3SMichael Baum 		/* Implement VLAN tag insertion as part inline data. */
1344256fb0e3SMichael Baum 		mlx5_tx_mseg_memcpy(pdst, loc,
1345256fb0e3SMichael Baum 				    2 * RTE_ETHER_ADDR_LEN,
1346256fb0e3SMichael Baum 				    2 * RTE_ETHER_ADDR_LEN, olx);
1347256fb0e3SMichael Baum 		pdst += 2 * RTE_ETHER_ADDR_LEN;
1348256fb0e3SMichael Baum 		*(unaligned_uint32_t *)pdst = rte_cpu_to_be_32
1349256fb0e3SMichael Baum 						((RTE_ETHER_TYPE_VLAN << 16) |
1350256fb0e3SMichael Baum 						 loc->mbuf->vlan_tci);
1351256fb0e3SMichael Baum 		pdst += sizeof(struct rte_vlan_hdr);
1352256fb0e3SMichael Baum 		tlen += 2 * RTE_ETHER_ADDR_LEN + sizeof(struct rte_vlan_hdr);
1353256fb0e3SMichael Baum 	}
1354256fb0e3SMichael Baum 	MLX5_ASSERT(pdst < (uint8_t *)txq->wqes_end);
1355256fb0e3SMichael Baum 	/*
1356256fb0e3SMichael Baum 	 * The WQEBB space availability is checked by caller.
1357256fb0e3SMichael Baum 	 * Here we should be aware of WQE ring buffer wraparound only.
1358256fb0e3SMichael Baum 	 */
1359256fb0e3SMichael Baum 	part = (uint8_t *)txq->wqes_end - pdst;
1360256fb0e3SMichael Baum 	part = RTE_MIN(part, inlen - tlen);
1361256fb0e3SMichael Baum 	MLX5_ASSERT(part);
1362256fb0e3SMichael Baum 	do {
1363256fb0e3SMichael Baum 		unsigned int copy;
1364256fb0e3SMichael Baum 
1365256fb0e3SMichael Baum 		/*
1366256fb0e3SMichael Baum 		 * Copying may be interrupted inside the routine
1367256fb0e3SMichael Baum 		 * if run into no inline hint flag.
1368256fb0e3SMichael Baum 		 */
136952e1ece5SViacheslav Ovsiienko 		copy = tso ? inlen : txq->inlen_mode;
137052e1ece5SViacheslav Ovsiienko 		copy = tlen >= copy ? 0 : (copy - tlen);
1371256fb0e3SMichael Baum 		copy = mlx5_tx_mseg_memcpy(pdst, loc, part, copy, olx);
1372256fb0e3SMichael Baum 		tlen += copy;
1373256fb0e3SMichael Baum 		if (likely(inlen <= tlen) || copy < part) {
1374256fb0e3SMichael Baum 			es->inline_hdr_sz = rte_cpu_to_be_16(tlen);
1375256fb0e3SMichael Baum 			pdst += copy;
1376256fb0e3SMichael Baum 			pdst = RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE);
1377256fb0e3SMichael Baum 			return (struct mlx5_wqe_dseg *)pdst;
1378256fb0e3SMichael Baum 		}
1379256fb0e3SMichael Baum 		pdst = (uint8_t *)txq->wqes;
1380256fb0e3SMichael Baum 		part = inlen - tlen;
1381256fb0e3SMichael Baum 	} while (true);
1382256fb0e3SMichael Baum }
1383256fb0e3SMichael Baum 
1384256fb0e3SMichael Baum /**
1385256fb0e3SMichael Baum  * Build the Data Segment of pointer type.
1386256fb0e3SMichael Baum  *
1387256fb0e3SMichael Baum  * @param txq
1388256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1389256fb0e3SMichael Baum  * @param loc
1390256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1391256fb0e3SMichael Baum  * @param dseg
1392256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Data Segment.
1393256fb0e3SMichael Baum  * @param buf
1394256fb0e3SMichael Baum  *   Data buffer to point.
1395256fb0e3SMichael Baum  * @param len
1396256fb0e3SMichael Baum  *   Data buffer length.
1397256fb0e3SMichael Baum  * @param olx
1398256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1399256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1400256fb0e3SMichael Baum  */
1401256fb0e3SMichael Baum static __rte_always_inline void
1402256fb0e3SMichael Baum mlx5_tx_dseg_ptr(struct mlx5_txq_data *__rte_restrict txq,
1403256fb0e3SMichael Baum 		 struct mlx5_txq_local *__rte_restrict loc,
1404256fb0e3SMichael Baum 		 struct mlx5_wqe_dseg *__rte_restrict dseg,
1405256fb0e3SMichael Baum 		 uint8_t *buf,
1406256fb0e3SMichael Baum 		 unsigned int len,
1407256fb0e3SMichael Baum 		 unsigned int olx __rte_unused)
1408256fb0e3SMichael Baum 
1409256fb0e3SMichael Baum {
1410256fb0e3SMichael Baum 	MLX5_ASSERT(len);
1411256fb0e3SMichael Baum 	dseg->bcount = rte_cpu_to_be_32(len);
141220489176SMichael Baum 	dseg->lkey = mlx5_mr_mb2mr(&txq->mr_ctrl, loc->mbuf);
1413256fb0e3SMichael Baum 	dseg->pbuf = rte_cpu_to_be_64((uintptr_t)buf);
1414256fb0e3SMichael Baum }
1415256fb0e3SMichael Baum 
1416256fb0e3SMichael Baum /**
1417256fb0e3SMichael Baum  * Build the Data Segment of pointer type or inline if data length is less than
1418256fb0e3SMichael Baum  * buffer in minimal Data Segment size.
1419256fb0e3SMichael Baum  *
1420256fb0e3SMichael Baum  * @param txq
1421256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1422256fb0e3SMichael Baum  * @param loc
1423256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1424256fb0e3SMichael Baum  * @param dseg
1425256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Data Segment.
1426256fb0e3SMichael Baum  * @param buf
1427256fb0e3SMichael Baum  *   Data buffer to point.
1428256fb0e3SMichael Baum  * @param len
1429256fb0e3SMichael Baum  *   Data buffer length.
1430256fb0e3SMichael Baum  * @param olx
1431256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1432256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1433256fb0e3SMichael Baum  */
1434256fb0e3SMichael Baum static __rte_always_inline void
1435256fb0e3SMichael Baum mlx5_tx_dseg_iptr(struct mlx5_txq_data *__rte_restrict txq,
1436256fb0e3SMichael Baum 		  struct mlx5_txq_local *__rte_restrict loc,
1437256fb0e3SMichael Baum 		  struct mlx5_wqe_dseg *__rte_restrict dseg,
1438256fb0e3SMichael Baum 		  uint8_t *buf,
1439256fb0e3SMichael Baum 		  unsigned int len,
1440256fb0e3SMichael Baum 		  unsigned int olx __rte_unused)
1441256fb0e3SMichael Baum 
1442256fb0e3SMichael Baum {
1443256fb0e3SMichael Baum 	uintptr_t dst, src;
1444256fb0e3SMichael Baum 
1445256fb0e3SMichael Baum 	MLX5_ASSERT(len);
1446256fb0e3SMichael Baum 	if (len > MLX5_DSEG_MIN_INLINE_SIZE) {
1447256fb0e3SMichael Baum 		dseg->bcount = rte_cpu_to_be_32(len);
144820489176SMichael Baum 		dseg->lkey = mlx5_mr_mb2mr(&txq->mr_ctrl, loc->mbuf);
1449256fb0e3SMichael Baum 		dseg->pbuf = rte_cpu_to_be_64((uintptr_t)buf);
1450256fb0e3SMichael Baum 
1451256fb0e3SMichael Baum 		return;
1452256fb0e3SMichael Baum 	}
1453256fb0e3SMichael Baum 	dseg->bcount = rte_cpu_to_be_32(len | MLX5_ETH_WQE_DATA_INLINE);
1454256fb0e3SMichael Baum 	/* Unrolled implementation of generic rte_memcpy. */
1455256fb0e3SMichael Baum 	dst = (uintptr_t)&dseg->inline_data[0];
1456256fb0e3SMichael Baum 	src = (uintptr_t)buf;
1457256fb0e3SMichael Baum 	if (len & 0x08) {
1458256fb0e3SMichael Baum #ifdef RTE_ARCH_STRICT_ALIGN
1459256fb0e3SMichael Baum 		MLX5_ASSERT(dst == RTE_PTR_ALIGN(dst, sizeof(uint32_t)));
1460256fb0e3SMichael Baum 		*(uint32_t *)dst = *(unaligned_uint32_t *)src;
1461256fb0e3SMichael Baum 		dst += sizeof(uint32_t);
1462256fb0e3SMichael Baum 		src += sizeof(uint32_t);
1463256fb0e3SMichael Baum 		*(uint32_t *)dst = *(unaligned_uint32_t *)src;
1464256fb0e3SMichael Baum 		dst += sizeof(uint32_t);
1465256fb0e3SMichael Baum 		src += sizeof(uint32_t);
1466256fb0e3SMichael Baum #else
1467256fb0e3SMichael Baum 		*(uint64_t *)dst = *(unaligned_uint64_t *)src;
1468256fb0e3SMichael Baum 		dst += sizeof(uint64_t);
1469256fb0e3SMichael Baum 		src += sizeof(uint64_t);
1470256fb0e3SMichael Baum #endif
1471256fb0e3SMichael Baum 	}
1472256fb0e3SMichael Baum 	if (len & 0x04) {
1473256fb0e3SMichael Baum 		*(uint32_t *)dst = *(unaligned_uint32_t *)src;
1474256fb0e3SMichael Baum 		dst += sizeof(uint32_t);
1475256fb0e3SMichael Baum 		src += sizeof(uint32_t);
1476256fb0e3SMichael Baum 	}
1477256fb0e3SMichael Baum 	if (len & 0x02) {
1478256fb0e3SMichael Baum 		*(uint16_t *)dst = *(unaligned_uint16_t *)src;
1479256fb0e3SMichael Baum 		dst += sizeof(uint16_t);
1480256fb0e3SMichael Baum 		src += sizeof(uint16_t);
1481256fb0e3SMichael Baum 	}
1482256fb0e3SMichael Baum 	if (len & 0x01)
1483256fb0e3SMichael Baum 		*(uint8_t *)dst = *(uint8_t *)src;
1484256fb0e3SMichael Baum }
1485256fb0e3SMichael Baum 
1486256fb0e3SMichael Baum /**
1487256fb0e3SMichael Baum  * Build the Data Segment of inlined data from single
1488256fb0e3SMichael Baum  * segment packet, no VLAN insertion.
1489256fb0e3SMichael Baum  *
1490256fb0e3SMichael Baum  * @param txq
1491256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1492256fb0e3SMichael Baum  * @param loc
1493256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1494256fb0e3SMichael Baum  * @param dseg
1495256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Data Segment.
1496256fb0e3SMichael Baum  * @param buf
1497256fb0e3SMichael Baum  *   Data buffer to point.
1498256fb0e3SMichael Baum  * @param len
1499256fb0e3SMichael Baum  *   Data buffer length.
1500256fb0e3SMichael Baum  * @param olx
1501256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1502256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1503256fb0e3SMichael Baum  *
1504256fb0e3SMichael Baum  * @return
1505256fb0e3SMichael Baum  *   Pointer to the next Data Segment after inlined data.
1506256fb0e3SMichael Baum  *   Ring buffer wraparound check is needed. We do not do it here because it
1507256fb0e3SMichael Baum  *   may not be needed for the last packet in the eMPW session.
1508256fb0e3SMichael Baum  */
1509256fb0e3SMichael Baum static __rte_always_inline struct mlx5_wqe_dseg *
1510256fb0e3SMichael Baum mlx5_tx_dseg_empw(struct mlx5_txq_data *__rte_restrict txq,
1511256fb0e3SMichael Baum 		  struct mlx5_txq_local *__rte_restrict loc __rte_unused,
1512256fb0e3SMichael Baum 		  struct mlx5_wqe_dseg *__rte_restrict dseg,
1513256fb0e3SMichael Baum 		  uint8_t *buf,
1514256fb0e3SMichael Baum 		  unsigned int len,
1515256fb0e3SMichael Baum 		  unsigned int olx __rte_unused)
1516256fb0e3SMichael Baum {
1517256fb0e3SMichael Baum 	unsigned int part;
1518256fb0e3SMichael Baum 	uint8_t *pdst;
1519256fb0e3SMichael Baum 
1520256fb0e3SMichael Baum 	if (!MLX5_TXOFF_CONFIG(MPW)) {
1521256fb0e3SMichael Baum 		/* Store the descriptor byte counter for eMPW sessions. */
1522256fb0e3SMichael Baum 		dseg->bcount = rte_cpu_to_be_32(len | MLX5_ETH_WQE_DATA_INLINE);
1523256fb0e3SMichael Baum 		pdst = &dseg->inline_data[0];
1524256fb0e3SMichael Baum 	} else {
1525256fb0e3SMichael Baum 		/* The entire legacy MPW session counter is stored on close. */
1526256fb0e3SMichael Baum 		pdst = (uint8_t *)dseg;
1527256fb0e3SMichael Baum 	}
1528256fb0e3SMichael Baum 	/*
1529256fb0e3SMichael Baum 	 * The WQEBB space availability is checked by caller.
1530256fb0e3SMichael Baum 	 * Here we should be aware of WQE ring buffer wraparound only.
1531256fb0e3SMichael Baum 	 */
1532256fb0e3SMichael Baum 	part = (uint8_t *)txq->wqes_end - pdst;
1533256fb0e3SMichael Baum 	part = RTE_MIN(part, len);
1534256fb0e3SMichael Baum 	do {
1535256fb0e3SMichael Baum 		rte_memcpy(pdst, buf, part);
1536256fb0e3SMichael Baum 		len -= part;
1537256fb0e3SMichael Baum 		if (likely(!len)) {
1538256fb0e3SMichael Baum 			pdst += part;
1539256fb0e3SMichael Baum 			if (!MLX5_TXOFF_CONFIG(MPW))
1540256fb0e3SMichael Baum 				pdst = RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE);
1541256fb0e3SMichael Baum 			/* Note: no final wraparound check here. */
1542256fb0e3SMichael Baum 			return (struct mlx5_wqe_dseg *)pdst;
1543256fb0e3SMichael Baum 		}
1544256fb0e3SMichael Baum 		pdst = (uint8_t *)txq->wqes;
1545256fb0e3SMichael Baum 		buf += part;
1546256fb0e3SMichael Baum 		part = len;
1547256fb0e3SMichael Baum 	} while (true);
1548256fb0e3SMichael Baum }
1549256fb0e3SMichael Baum 
1550256fb0e3SMichael Baum /**
1551256fb0e3SMichael Baum  * Build the Data Segment of inlined data from single
1552256fb0e3SMichael Baum  * segment packet with VLAN insertion.
1553256fb0e3SMichael Baum  *
1554256fb0e3SMichael Baum  * @param txq
1555256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1556256fb0e3SMichael Baum  * @param loc
1557256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1558256fb0e3SMichael Baum  * @param dseg
1559256fb0e3SMichael Baum  *   Pointer to the dseg fill with built Data Segment.
1560256fb0e3SMichael Baum  * @param buf
1561256fb0e3SMichael Baum  *   Data buffer to point.
1562256fb0e3SMichael Baum  * @param len
1563256fb0e3SMichael Baum  *   Data buffer length.
1564256fb0e3SMichael Baum  * @param olx
1565256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1566256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1567256fb0e3SMichael Baum  *
1568256fb0e3SMichael Baum  * @return
1569256fb0e3SMichael Baum  *   Pointer to the next Data Segment after inlined data.
1570256fb0e3SMichael Baum  *   Ring buffer wraparound check is needed.
1571256fb0e3SMichael Baum  */
1572256fb0e3SMichael Baum static __rte_always_inline struct mlx5_wqe_dseg *
1573256fb0e3SMichael Baum mlx5_tx_dseg_vlan(struct mlx5_txq_data *__rte_restrict txq,
1574256fb0e3SMichael Baum 		  struct mlx5_txq_local *__rte_restrict loc __rte_unused,
1575256fb0e3SMichael Baum 		  struct mlx5_wqe_dseg *__rte_restrict dseg,
1576256fb0e3SMichael Baum 		  uint8_t *buf,
1577256fb0e3SMichael Baum 		  unsigned int len,
1578256fb0e3SMichael Baum 		  unsigned int olx __rte_unused)
1579256fb0e3SMichael Baum 
1580256fb0e3SMichael Baum {
1581256fb0e3SMichael Baum 	unsigned int part;
1582256fb0e3SMichael Baum 	uint8_t *pdst;
1583256fb0e3SMichael Baum 
1584256fb0e3SMichael Baum 	MLX5_ASSERT(len > MLX5_ESEG_MIN_INLINE_SIZE);
1585256fb0e3SMichael Baum 	if (!MLX5_TXOFF_CONFIG(MPW)) {
1586256fb0e3SMichael Baum 		/* Store the descriptor byte counter for eMPW sessions. */
1587256fb0e3SMichael Baum 		dseg->bcount = rte_cpu_to_be_32
1588256fb0e3SMichael Baum 				((len + sizeof(struct rte_vlan_hdr)) |
1589256fb0e3SMichael Baum 				 MLX5_ETH_WQE_DATA_INLINE);
1590256fb0e3SMichael Baum 		pdst = &dseg->inline_data[0];
1591256fb0e3SMichael Baum 	} else {
1592256fb0e3SMichael Baum 		/* The entire legacy MPW session counter is stored on close. */
1593256fb0e3SMichael Baum 		pdst = (uint8_t *)dseg;
1594256fb0e3SMichael Baum 	}
1595256fb0e3SMichael Baum 	memcpy(pdst, buf, MLX5_DSEG_MIN_INLINE_SIZE);
1596256fb0e3SMichael Baum 	buf += MLX5_DSEG_MIN_INLINE_SIZE;
1597256fb0e3SMichael Baum 	pdst += MLX5_DSEG_MIN_INLINE_SIZE;
1598256fb0e3SMichael Baum 	len -= MLX5_DSEG_MIN_INLINE_SIZE;
1599256fb0e3SMichael Baum 	/* Insert VLAN ethertype + VLAN tag. Pointer is aligned. */
1600256fb0e3SMichael Baum 	MLX5_ASSERT(pdst == RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE));
1601256fb0e3SMichael Baum 	if (unlikely(pdst >= (uint8_t *)txq->wqes_end))
1602256fb0e3SMichael Baum 		pdst = (uint8_t *)txq->wqes;
1603256fb0e3SMichael Baum 	*(uint32_t *)pdst = rte_cpu_to_be_32((RTE_ETHER_TYPE_VLAN << 16) |
1604256fb0e3SMichael Baum 					      loc->mbuf->vlan_tci);
1605256fb0e3SMichael Baum 	pdst += sizeof(struct rte_vlan_hdr);
1606256fb0e3SMichael Baum 	/*
1607256fb0e3SMichael Baum 	 * The WQEBB space availability is checked by caller.
1608256fb0e3SMichael Baum 	 * Here we should be aware of WQE ring buffer wraparound only.
1609256fb0e3SMichael Baum 	 */
1610256fb0e3SMichael Baum 	part = (uint8_t *)txq->wqes_end - pdst;
1611256fb0e3SMichael Baum 	part = RTE_MIN(part, len);
1612256fb0e3SMichael Baum 	do {
1613256fb0e3SMichael Baum 		rte_memcpy(pdst, buf, part);
1614256fb0e3SMichael Baum 		len -= part;
1615256fb0e3SMichael Baum 		if (likely(!len)) {
1616256fb0e3SMichael Baum 			pdst += part;
1617256fb0e3SMichael Baum 			if (!MLX5_TXOFF_CONFIG(MPW))
1618256fb0e3SMichael Baum 				pdst = RTE_PTR_ALIGN(pdst, MLX5_WSEG_SIZE);
1619256fb0e3SMichael Baum 			/* Note: no final wraparound check here. */
1620256fb0e3SMichael Baum 			return (struct mlx5_wqe_dseg *)pdst;
1621256fb0e3SMichael Baum 		}
1622256fb0e3SMichael Baum 		pdst = (uint8_t *)txq->wqes;
1623256fb0e3SMichael Baum 		buf += part;
1624256fb0e3SMichael Baum 		part = len;
1625256fb0e3SMichael Baum 	} while (true);
1626256fb0e3SMichael Baum }
1627256fb0e3SMichael Baum 
1628256fb0e3SMichael Baum /**
1629256fb0e3SMichael Baum  * Build the Ethernet Segment with optionally inlined data with
1630256fb0e3SMichael Baum  * VLAN insertion and following Data Segments (if any) from
1631256fb0e3SMichael Baum  * multi-segment packet. Used by ordinary send and TSO.
1632256fb0e3SMichael Baum  *
1633256fb0e3SMichael Baum  * @param txq
1634256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1635256fb0e3SMichael Baum  * @param loc
1636256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1637256fb0e3SMichael Baum  * @param wqe
1638256fb0e3SMichael Baum  *   Pointer to WQE to fill with built Ethernet/Data Segments.
1639256fb0e3SMichael Baum  * @param vlan
1640256fb0e3SMichael Baum  *   Length of VLAN header to insert, 0 means no VLAN insertion.
1641256fb0e3SMichael Baum  * @param inlen
1642256fb0e3SMichael Baum  *   Data length to inline. For TSO this parameter specifies exact value,
1643256fb0e3SMichael Baum  *   for ordinary send routine can be aligned by caller to provide better WQE
1644256fb0e3SMichael Baum  *   space saving and data buffer start address alignment.
1645256fb0e3SMichael Baum  *   This length includes VLAN header being inserted.
1646256fb0e3SMichael Baum  * @param tso
1647256fb0e3SMichael Baum  *   Zero means ordinary send, inlined data can be extended,
1648256fb0e3SMichael Baum  *   otherwise this is TSO, inlined data length is fixed.
1649256fb0e3SMichael Baum  * @param olx
1650256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1651256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1652256fb0e3SMichael Baum  *
1653256fb0e3SMichael Baum  * @return
1654256fb0e3SMichael Baum  *   Actual size of built WQE in segments.
1655256fb0e3SMichael Baum  */
1656256fb0e3SMichael Baum static __rte_always_inline unsigned int
1657256fb0e3SMichael Baum mlx5_tx_mseg_build(struct mlx5_txq_data *__rte_restrict txq,
1658256fb0e3SMichael Baum 		   struct mlx5_txq_local *__rte_restrict loc,
1659256fb0e3SMichael Baum 		   struct mlx5_wqe *__rte_restrict wqe,
1660256fb0e3SMichael Baum 		   unsigned int vlan,
1661256fb0e3SMichael Baum 		   unsigned int inlen,
1662256fb0e3SMichael Baum 		   unsigned int tso,
1663256fb0e3SMichael Baum 		   unsigned int olx __rte_unused)
1664256fb0e3SMichael Baum {
1665256fb0e3SMichael Baum 	struct mlx5_wqe_dseg *__rte_restrict dseg;
1666256fb0e3SMichael Baum 	unsigned int ds;
1667256fb0e3SMichael Baum 
1668256fb0e3SMichael Baum 	MLX5_ASSERT((rte_pktmbuf_pkt_len(loc->mbuf) + vlan) >= inlen);
1669256fb0e3SMichael Baum 	loc->mbuf_nseg = NB_SEGS(loc->mbuf);
1670256fb0e3SMichael Baum 	loc->mbuf_off = 0;
1671256fb0e3SMichael Baum 
1672256fb0e3SMichael Baum 	dseg = mlx5_tx_eseg_mdat(txq, loc, wqe, vlan, inlen, tso, olx);
1673256fb0e3SMichael Baum 	if (!loc->mbuf_nseg)
1674256fb0e3SMichael Baum 		goto dseg_done;
1675256fb0e3SMichael Baum 	/*
1676256fb0e3SMichael Baum 	 * There are still some mbuf remaining, not inlined.
1677256fb0e3SMichael Baum 	 * The first mbuf may be partially inlined and we
1678256fb0e3SMichael Baum 	 * must process the possible non-zero data offset.
1679256fb0e3SMichael Baum 	 */
1680256fb0e3SMichael Baum 	if (loc->mbuf_off) {
1681256fb0e3SMichael Baum 		unsigned int dlen;
1682256fb0e3SMichael Baum 		uint8_t *dptr;
1683256fb0e3SMichael Baum 
1684256fb0e3SMichael Baum 		/*
1685256fb0e3SMichael Baum 		 * Exhausted packets must be dropped before.
1686256fb0e3SMichael Baum 		 * Non-zero offset means there are some data
1687256fb0e3SMichael Baum 		 * remained in the packet.
1688256fb0e3SMichael Baum 		 */
1689256fb0e3SMichael Baum 		MLX5_ASSERT(loc->mbuf_off < rte_pktmbuf_data_len(loc->mbuf));
1690256fb0e3SMichael Baum 		MLX5_ASSERT(rte_pktmbuf_data_len(loc->mbuf));
1691256fb0e3SMichael Baum 		dptr = rte_pktmbuf_mtod_offset(loc->mbuf, uint8_t *,
1692256fb0e3SMichael Baum 					       loc->mbuf_off);
1693256fb0e3SMichael Baum 		dlen = rte_pktmbuf_data_len(loc->mbuf) - loc->mbuf_off;
1694256fb0e3SMichael Baum 		/*
1695256fb0e3SMichael Baum 		 * Build the pointer/minimal Data Segment.
1696256fb0e3SMichael Baum 		 * Do ring buffer wrapping check in advance.
1697256fb0e3SMichael Baum 		 */
1698256fb0e3SMichael Baum 		if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
1699256fb0e3SMichael Baum 			dseg = (struct mlx5_wqe_dseg *)txq->wqes;
1700256fb0e3SMichael Baum 		mlx5_tx_dseg_iptr(txq, loc, dseg, dptr, dlen, olx);
1701256fb0e3SMichael Baum 		/* Store the mbuf to be freed on completion. */
1702256fb0e3SMichael Baum 		MLX5_ASSERT(loc->elts_free);
1703256fb0e3SMichael Baum 		txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
1704256fb0e3SMichael Baum 		--loc->elts_free;
1705256fb0e3SMichael Baum 		++dseg;
1706256fb0e3SMichael Baum 		if (--loc->mbuf_nseg == 0)
1707256fb0e3SMichael Baum 			goto dseg_done;
1708256fb0e3SMichael Baum 		loc->mbuf = loc->mbuf->next;
1709256fb0e3SMichael Baum 		loc->mbuf_off = 0;
1710256fb0e3SMichael Baum 	}
1711256fb0e3SMichael Baum 	do {
1712256fb0e3SMichael Baum 		if (unlikely(!rte_pktmbuf_data_len(loc->mbuf))) {
1713256fb0e3SMichael Baum 			struct rte_mbuf *mbuf;
1714256fb0e3SMichael Baum 
1715256fb0e3SMichael Baum 			/* Zero length segment found, just skip. */
1716256fb0e3SMichael Baum 			mbuf = loc->mbuf;
1717256fb0e3SMichael Baum 			loc->mbuf = loc->mbuf->next;
1718256fb0e3SMichael Baum 			rte_pktmbuf_free_seg(mbuf);
1719256fb0e3SMichael Baum 			if (--loc->mbuf_nseg == 0)
1720256fb0e3SMichael Baum 				break;
1721256fb0e3SMichael Baum 		} else {
1722256fb0e3SMichael Baum 			if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
1723256fb0e3SMichael Baum 				dseg = (struct mlx5_wqe_dseg *)txq->wqes;
1724256fb0e3SMichael Baum 			mlx5_tx_dseg_iptr
1725256fb0e3SMichael Baum 				(txq, loc, dseg,
1726256fb0e3SMichael Baum 				 rte_pktmbuf_mtod(loc->mbuf, uint8_t *),
1727256fb0e3SMichael Baum 				 rte_pktmbuf_data_len(loc->mbuf), olx);
1728256fb0e3SMichael Baum 			MLX5_ASSERT(loc->elts_free);
1729256fb0e3SMichael Baum 			txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
1730256fb0e3SMichael Baum 			--loc->elts_free;
1731256fb0e3SMichael Baum 			++dseg;
1732256fb0e3SMichael Baum 			if (--loc->mbuf_nseg == 0)
1733256fb0e3SMichael Baum 				break;
1734256fb0e3SMichael Baum 			loc->mbuf = loc->mbuf->next;
1735256fb0e3SMichael Baum 		}
1736256fb0e3SMichael Baum 	} while (true);
1737256fb0e3SMichael Baum 
1738256fb0e3SMichael Baum dseg_done:
1739256fb0e3SMichael Baum 	/* Calculate actual segments used from the dseg pointer. */
1740256fb0e3SMichael Baum 	if ((uintptr_t)wqe < (uintptr_t)dseg)
1741256fb0e3SMichael Baum 		ds = ((uintptr_t)dseg - (uintptr_t)wqe) / MLX5_WSEG_SIZE;
1742256fb0e3SMichael Baum 	else
1743256fb0e3SMichael Baum 		ds = (((uintptr_t)dseg - (uintptr_t)wqe) +
1744256fb0e3SMichael Baum 		      txq->wqe_s * MLX5_WQE_SIZE) / MLX5_WSEG_SIZE;
1745256fb0e3SMichael Baum 	return ds;
1746256fb0e3SMichael Baum }
1747256fb0e3SMichael Baum 
1748256fb0e3SMichael Baum /**
1749256fb0e3SMichael Baum  * The routine checks timestamp flag in the current packet,
1750256fb0e3SMichael Baum  * and push WAIT WQE into the queue if scheduling is required.
1751256fb0e3SMichael Baum  *
1752256fb0e3SMichael Baum  * @param txq
1753256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1754256fb0e3SMichael Baum  * @param loc
1755256fb0e3SMichael Baum  *   Pointer to burst routine local context.
175637d6fc30SViacheslav Ovsiienko  * @param elts
175737d6fc30SViacheslav Ovsiienko  *   Number of free elements in elts buffer to be checked, for zero
175837d6fc30SViacheslav Ovsiienko  *   value the check is optimized out by compiler.
1759256fb0e3SMichael Baum  * @param olx
1760256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1761256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1762256fb0e3SMichael Baum  *
1763256fb0e3SMichael Baum  * @return
1764256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
1765256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_SINGLE - continue processing with the packet.
1766256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_MULTI - the WAIT inserted, continue processing.
1767256fb0e3SMichael Baum  * Local context variables partially updated.
1768256fb0e3SMichael Baum  */
1769256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
1770256fb0e3SMichael Baum mlx5_tx_schedule_send(struct mlx5_txq_data *restrict txq,
1771256fb0e3SMichael Baum 		      struct mlx5_txq_local *restrict loc,
177237d6fc30SViacheslav Ovsiienko 		      uint16_t elts,
1773256fb0e3SMichael Baum 		      unsigned int olx)
1774256fb0e3SMichael Baum {
1775256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(TXPP) &&
1776256fb0e3SMichael Baum 	    loc->mbuf->ol_flags & txq->ts_mask) {
177749e87976SViacheslav Ovsiienko 		struct mlx5_dev_ctx_shared *sh;
1778256fb0e3SMichael Baum 		struct mlx5_wqe *wqe;
1779256fb0e3SMichael Baum 		uint64_t ts;
1780256fb0e3SMichael Baum 
1781256fb0e3SMichael Baum 		/*
1782256fb0e3SMichael Baum 		 * Estimate the required space quickly and roughly.
1783256fb0e3SMichael Baum 		 * We would like to ensure the packet can be pushed
1784256fb0e3SMichael Baum 		 * to the queue and we won't get the orphan WAIT WQE.
1785256fb0e3SMichael Baum 		 */
1786256fb0e3SMichael Baum 		if (loc->wqe_free <= MLX5_WQE_SIZE_MAX / MLX5_WQE_SIZE ||
178737d6fc30SViacheslav Ovsiienko 		    loc->elts_free < elts)
1788256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
1789256fb0e3SMichael Baum 		/* Convert the timestamp into completion to wait. */
1790256fb0e3SMichael Baum 		ts = *RTE_MBUF_DYNFIELD(loc->mbuf, txq->ts_offset, uint64_t *);
1791a31aa37bSViacheslav Ovsiienko 		if (txq->ts_last && ts < txq->ts_last)
1792e12a0166STyler Retzlaff 			rte_atomic_fetch_add_explicit(&txq->sh->txpp.err_ts_order,
1793e12a0166STyler Retzlaff 					   1, rte_memory_order_relaxed);
1794a31aa37bSViacheslav Ovsiienko 		txq->ts_last = ts;
179549e87976SViacheslav Ovsiienko 		wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
179649e87976SViacheslav Ovsiienko 		sh = txq->sh;
179749e87976SViacheslav Ovsiienko 		if (txq->wait_on_time) {
179849e87976SViacheslav Ovsiienko 			/* The wait on time capability should be used. */
179949e87976SViacheslav Ovsiienko 			ts -= sh->txpp.skew;
1800a1e910f5SViacheslav Ovsiienko 			rte_pmd_mlx5_trace_tx_wait(ts);
180149e87976SViacheslav Ovsiienko 			mlx5_tx_cseg_init(txq, loc, wqe,
180249e87976SViacheslav Ovsiienko 					  1 + sizeof(struct mlx5_wqe_wseg) /
180349e87976SViacheslav Ovsiienko 					      MLX5_WSEG_SIZE,
180449e87976SViacheslav Ovsiienko 					  MLX5_OPCODE_WAIT |
180549e87976SViacheslav Ovsiienko 					  MLX5_OPC_MOD_WAIT_TIME << 24, olx);
180649e87976SViacheslav Ovsiienko 			mlx5_tx_wseg_init(txq, loc, wqe, ts, olx);
180749e87976SViacheslav Ovsiienko 		} else {
180849e87976SViacheslav Ovsiienko 			/* Legacy cross-channel operation should be used. */
180949e87976SViacheslav Ovsiienko 			int32_t wci;
181049e87976SViacheslav Ovsiienko 
181149e87976SViacheslav Ovsiienko 			wci = mlx5_txpp_convert_tx_ts(sh, ts);
1812256fb0e3SMichael Baum 			if (unlikely(wci < 0))
1813256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_SINGLE;
1814256fb0e3SMichael Baum 			/* Build the WAIT WQE with specified completion. */
1815a1e910f5SViacheslav Ovsiienko 			rte_pmd_mlx5_trace_tx_wait(ts - sh->txpp.skew);
181649e87976SViacheslav Ovsiienko 			mlx5_tx_cseg_init(txq, loc, wqe,
181749e87976SViacheslav Ovsiienko 					  1 + sizeof(struct mlx5_wqe_qseg) /
181849e87976SViacheslav Ovsiienko 					      MLX5_WSEG_SIZE,
181949e87976SViacheslav Ovsiienko 					  MLX5_OPCODE_WAIT |
182049e87976SViacheslav Ovsiienko 					  MLX5_OPC_MOD_WAIT_CQ_PI << 24, olx);
182149e87976SViacheslav Ovsiienko 			mlx5_tx_qseg_init(txq, loc, wqe, wci, olx);
182249e87976SViacheslav Ovsiienko 		}
1823256fb0e3SMichael Baum 		++txq->wqe_ci;
1824256fb0e3SMichael Baum 		--loc->wqe_free;
1825256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_MULTI;
1826256fb0e3SMichael Baum 	}
1827256fb0e3SMichael Baum 	return MLX5_TXCMP_CODE_SINGLE;
1828256fb0e3SMichael Baum }
1829256fb0e3SMichael Baum 
1830256fb0e3SMichael Baum /**
1831256fb0e3SMichael Baum  * Tx one packet function for multi-segment TSO. Supports all
1832256fb0e3SMichael Baum  * types of Tx offloads, uses MLX5_OPCODE_TSO to build WQEs,
1833256fb0e3SMichael Baum  * sends one packet per WQE.
1834256fb0e3SMichael Baum  *
1835256fb0e3SMichael Baum  * This routine is responsible for storing processed mbuf
1836256fb0e3SMichael Baum  * into elts ring buffer and update elts_head.
1837256fb0e3SMichael Baum  *
1838256fb0e3SMichael Baum  * @param txq
1839256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1840256fb0e3SMichael Baum  * @param loc
1841256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1842256fb0e3SMichael Baum  * @param olx
1843256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1844256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1845256fb0e3SMichael Baum  *
1846256fb0e3SMichael Baum  * @return
1847256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
1848256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
1849256fb0e3SMichael Baum  * Local context variables partially updated.
1850256fb0e3SMichael Baum  */
1851256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
1852256fb0e3SMichael Baum mlx5_tx_packet_multi_tso(struct mlx5_txq_data *__rte_restrict txq,
1853256fb0e3SMichael Baum 			struct mlx5_txq_local *__rte_restrict loc,
1854256fb0e3SMichael Baum 			unsigned int olx)
1855256fb0e3SMichael Baum {
1856256fb0e3SMichael Baum 	struct mlx5_wqe *__rte_restrict wqe;
1857256fb0e3SMichael Baum 	unsigned int ds, dlen, inlen, ntcp, vlan = 0;
1858256fb0e3SMichael Baum 
185937d6fc30SViacheslav Ovsiienko 	MLX5_ASSERT(loc->elts_free >= NB_SEGS(loc->mbuf));
1860256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(TXPP)) {
1861256fb0e3SMichael Baum 		enum mlx5_txcmp_code wret;
1862256fb0e3SMichael Baum 
1863256fb0e3SMichael Baum 		/* Generate WAIT for scheduling if requested. */
186437d6fc30SViacheslav Ovsiienko 		wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
1865256fb0e3SMichael Baum 		if (wret == MLX5_TXCMP_CODE_EXIT)
1866256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
1867256fb0e3SMichael Baum 		if (wret == MLX5_TXCMP_CODE_ERROR)
1868256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_ERROR;
1869256fb0e3SMichael Baum 	}
1870256fb0e3SMichael Baum 	/*
1871256fb0e3SMichael Baum 	 * Calculate data length to be inlined to estimate
1872256fb0e3SMichael Baum 	 * the required space in WQE ring buffer.
1873256fb0e3SMichael Baum 	 */
1874256fb0e3SMichael Baum 	dlen = rte_pktmbuf_pkt_len(loc->mbuf);
1875daa02b5cSOlivier Matz 	if (MLX5_TXOFF_CONFIG(VLAN) && loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN)
1876256fb0e3SMichael Baum 		vlan = sizeof(struct rte_vlan_hdr);
1877256fb0e3SMichael Baum 	inlen = loc->mbuf->l2_len + vlan +
1878256fb0e3SMichael Baum 		loc->mbuf->l3_len + loc->mbuf->l4_len;
1879256fb0e3SMichael Baum 	if (unlikely((!inlen || !loc->mbuf->tso_segsz)))
1880256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_ERROR;
1881daa02b5cSOlivier Matz 	if (loc->mbuf->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK)
1882256fb0e3SMichael Baum 		inlen += loc->mbuf->outer_l2_len + loc->mbuf->outer_l3_len;
1883256fb0e3SMichael Baum 	/* Packet must contain all TSO headers. */
1884256fb0e3SMichael Baum 	if (unlikely(inlen > MLX5_MAX_TSO_HEADER ||
1885256fb0e3SMichael Baum 		     inlen <= MLX5_ESEG_MIN_INLINE_SIZE ||
1886256fb0e3SMichael Baum 		     inlen > (dlen + vlan)))
1887256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_ERROR;
1888256fb0e3SMichael Baum 	/*
1889256fb0e3SMichael Baum 	 * Check whether there are enough free WQEBBs:
1890256fb0e3SMichael Baum 	 * - Control Segment
1891256fb0e3SMichael Baum 	 * - Ethernet Segment
1892256fb0e3SMichael Baum 	 * - First Segment of inlined Ethernet data
1893256fb0e3SMichael Baum 	 * - ... data continued ...
1894256fb0e3SMichael Baum 	 * - Data Segments of pointer/min inline type
1895256fb0e3SMichael Baum 	 */
1896256fb0e3SMichael Baum 	ds = NB_SEGS(loc->mbuf) + 2 + (inlen -
1897256fb0e3SMichael Baum 				       MLX5_ESEG_MIN_INLINE_SIZE +
1898256fb0e3SMichael Baum 				       MLX5_WSEG_SIZE +
1899256fb0e3SMichael Baum 				       MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
1900256fb0e3SMichael Baum 	if (unlikely(loc->wqe_free < ((ds + 3) / 4)))
1901256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_EXIT;
1902256fb0e3SMichael Baum 	/* Check for maximal WQE size. */
1903130bb7daSRaja Zidane 	if (unlikely((MLX5_WQE_SIZE_MAX / MLX5_WSEG_SIZE) < ds))
1904256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_ERROR;
1905256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
1906256fb0e3SMichael Baum 	/* Update sent data bytes/packets counters. */
1907256fb0e3SMichael Baum 	ntcp = (dlen - (inlen - vlan) + loc->mbuf->tso_segsz - 1) /
1908256fb0e3SMichael Baum 		loc->mbuf->tso_segsz;
1909256fb0e3SMichael Baum 	/*
1910256fb0e3SMichael Baum 	 * One will be added for mbuf itself at the end of the mlx5_tx_burst
1911256fb0e3SMichael Baum 	 * from loc->pkts_sent field.
1912256fb0e3SMichael Baum 	 */
1913256fb0e3SMichael Baum 	--ntcp;
1914256fb0e3SMichael Baum 	txq->stats.opackets += ntcp;
1915256fb0e3SMichael Baum 	txq->stats.obytes += dlen + vlan + ntcp * inlen;
1916256fb0e3SMichael Baum #endif
1917256fb0e3SMichael Baum 	wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
1918256fb0e3SMichael Baum 	loc->wqe_last = wqe;
1919256fb0e3SMichael Baum 	mlx5_tx_cseg_init(txq, loc, wqe, 0, MLX5_OPCODE_TSO, olx);
1920a1e910f5SViacheslav Ovsiienko 	rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
1921256fb0e3SMichael Baum 	ds = mlx5_tx_mseg_build(txq, loc, wqe, vlan, inlen, 1, olx);
1922256fb0e3SMichael Baum 	wqe->cseg.sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | ds);
1923256fb0e3SMichael Baum 	txq->wqe_ci += (ds + 3) / 4;
1924256fb0e3SMichael Baum 	loc->wqe_free -= (ds + 3) / 4;
1925256fb0e3SMichael Baum 	return MLX5_TXCMP_CODE_MULTI;
1926256fb0e3SMichael Baum }
1927256fb0e3SMichael Baum 
1928256fb0e3SMichael Baum /**
1929256fb0e3SMichael Baum  * Tx one packet function for multi-segment SEND. Supports all types of Tx
1930256fb0e3SMichael Baum  * offloads, uses MLX5_OPCODE_SEND to build WQEs, sends one packet per WQE,
1931256fb0e3SMichael Baum  * without any data inlining in Ethernet Segment.
1932256fb0e3SMichael Baum  *
1933256fb0e3SMichael Baum  * This routine is responsible for storing processed mbuf
1934256fb0e3SMichael Baum  * into elts ring buffer and update elts_head.
1935256fb0e3SMichael Baum  *
1936256fb0e3SMichael Baum  * @param txq
1937256fb0e3SMichael Baum  *   Pointer to TX queue structure.
1938256fb0e3SMichael Baum  * @param loc
1939256fb0e3SMichael Baum  *   Pointer to burst routine local context.
1940256fb0e3SMichael Baum  * @param olx
1941256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
1942256fb0e3SMichael Baum  *   compile time and may be used for optimization.
1943256fb0e3SMichael Baum  *
1944256fb0e3SMichael Baum  * @return
1945256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
1946256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
1947256fb0e3SMichael Baum  * Local context variables partially updated.
1948256fb0e3SMichael Baum  */
1949256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
1950256fb0e3SMichael Baum mlx5_tx_packet_multi_send(struct mlx5_txq_data *__rte_restrict txq,
1951256fb0e3SMichael Baum 			  struct mlx5_txq_local *__rte_restrict loc,
1952256fb0e3SMichael Baum 			  unsigned int olx)
1953256fb0e3SMichael Baum {
1954256fb0e3SMichael Baum 	struct mlx5_wqe_dseg *__rte_restrict dseg;
1955256fb0e3SMichael Baum 	struct mlx5_wqe *__rte_restrict wqe;
1956256fb0e3SMichael Baum 	unsigned int ds, nseg;
1957256fb0e3SMichael Baum 
1958256fb0e3SMichael Baum 	MLX5_ASSERT(NB_SEGS(loc->mbuf) > 1);
195937d6fc30SViacheslav Ovsiienko 	MLX5_ASSERT(loc->elts_free >= NB_SEGS(loc->mbuf));
1960256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(TXPP)) {
1961256fb0e3SMichael Baum 		enum mlx5_txcmp_code wret;
1962256fb0e3SMichael Baum 
1963256fb0e3SMichael Baum 		/* Generate WAIT for scheduling if requested. */
196437d6fc30SViacheslav Ovsiienko 		wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
1965256fb0e3SMichael Baum 		if (wret == MLX5_TXCMP_CODE_EXIT)
1966256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
1967256fb0e3SMichael Baum 		if (wret == MLX5_TXCMP_CODE_ERROR)
1968256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_ERROR;
1969256fb0e3SMichael Baum 	}
1970256fb0e3SMichael Baum 	/*
1971256fb0e3SMichael Baum 	 * No inline at all, it means the CPU cycles saving is prioritized at
1972256fb0e3SMichael Baum 	 * configuration, we should not copy any packet data to WQE.
1973256fb0e3SMichael Baum 	 */
1974256fb0e3SMichael Baum 	nseg = NB_SEGS(loc->mbuf);
1975256fb0e3SMichael Baum 	ds = 2 + nseg;
1976256fb0e3SMichael Baum 	if (unlikely(loc->wqe_free < ((ds + 3) / 4)))
1977256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_EXIT;
1978256fb0e3SMichael Baum 	/* Check for maximal WQE size. */
1979130bb7daSRaja Zidane 	if (unlikely((MLX5_WQE_SIZE_MAX / MLX5_WSEG_SIZE) < ds))
1980256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_ERROR;
1981256fb0e3SMichael Baum 	/*
1982256fb0e3SMichael Baum 	 * Some Tx offloads may cause an error if packet is not long enough,
1983256fb0e3SMichael Baum 	 * check against assumed minimal length.
1984256fb0e3SMichael Baum 	 */
1985256fb0e3SMichael Baum 	if (rte_pktmbuf_pkt_len(loc->mbuf) <= MLX5_ESEG_MIN_INLINE_SIZE)
1986256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_ERROR;
1987256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
1988256fb0e3SMichael Baum 	/* Update sent data bytes counter. */
1989256fb0e3SMichael Baum 	txq->stats.obytes += rte_pktmbuf_pkt_len(loc->mbuf);
1990256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(VLAN) &&
1991daa02b5cSOlivier Matz 	    loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN)
1992256fb0e3SMichael Baum 		txq->stats.obytes += sizeof(struct rte_vlan_hdr);
1993256fb0e3SMichael Baum #endif
1994256fb0e3SMichael Baum 	/*
1995256fb0e3SMichael Baum 	 * SEND WQE, one WQEBB:
1996256fb0e3SMichael Baum 	 * - Control Segment, SEND opcode
1997256fb0e3SMichael Baum 	 * - Ethernet Segment, optional VLAN, no inline
1998256fb0e3SMichael Baum 	 * - Data Segments, pointer only type
1999256fb0e3SMichael Baum 	 */
2000256fb0e3SMichael Baum 	wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
2001256fb0e3SMichael Baum 	loc->wqe_last = wqe;
2002256fb0e3SMichael Baum 	mlx5_tx_cseg_init(txq, loc, wqe, ds, MLX5_OPCODE_SEND, olx);
2003a1e910f5SViacheslav Ovsiienko 	rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
2004256fb0e3SMichael Baum 	mlx5_tx_eseg_none(txq, loc, wqe, olx);
2005256fb0e3SMichael Baum 	dseg = &wqe->dseg[0];
2006256fb0e3SMichael Baum 	do {
2007256fb0e3SMichael Baum 		if (unlikely(!rte_pktmbuf_data_len(loc->mbuf))) {
2008256fb0e3SMichael Baum 			struct rte_mbuf *mbuf;
2009256fb0e3SMichael Baum 
2010256fb0e3SMichael Baum 			/*
2011256fb0e3SMichael Baum 			 * Zero length segment found, have to correct total
2012256fb0e3SMichael Baum 			 * size of WQE in segments.
2013256fb0e3SMichael Baum 			 * It is supposed to be rare occasion, so in normal
2014256fb0e3SMichael Baum 			 * case (no zero length segments) we avoid extra
2015256fb0e3SMichael Baum 			 * writing to the Control Segment.
2016256fb0e3SMichael Baum 			 */
2017256fb0e3SMichael Baum 			--ds;
2018256fb0e3SMichael Baum 			wqe->cseg.sq_ds -= RTE_BE32(1);
2019256fb0e3SMichael Baum 			mbuf = loc->mbuf;
2020256fb0e3SMichael Baum 			loc->mbuf = mbuf->next;
2021256fb0e3SMichael Baum 			rte_pktmbuf_free_seg(mbuf);
2022256fb0e3SMichael Baum 			if (--nseg == 0)
2023256fb0e3SMichael Baum 				break;
2024256fb0e3SMichael Baum 		} else {
2025256fb0e3SMichael Baum 			mlx5_tx_dseg_ptr
2026256fb0e3SMichael Baum 				(txq, loc, dseg,
2027256fb0e3SMichael Baum 				 rte_pktmbuf_mtod(loc->mbuf, uint8_t *),
2028256fb0e3SMichael Baum 				 rte_pktmbuf_data_len(loc->mbuf), olx);
2029256fb0e3SMichael Baum 			txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
2030256fb0e3SMichael Baum 			--loc->elts_free;
2031256fb0e3SMichael Baum 			if (--nseg == 0)
2032256fb0e3SMichael Baum 				break;
2033256fb0e3SMichael Baum 			++dseg;
2034256fb0e3SMichael Baum 			if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
2035256fb0e3SMichael Baum 				dseg = (struct mlx5_wqe_dseg *)txq->wqes;
2036256fb0e3SMichael Baum 			loc->mbuf = loc->mbuf->next;
2037256fb0e3SMichael Baum 		}
2038256fb0e3SMichael Baum 	} while (true);
2039256fb0e3SMichael Baum 	txq->wqe_ci += (ds + 3) / 4;
2040256fb0e3SMichael Baum 	loc->wqe_free -= (ds + 3) / 4;
2041256fb0e3SMichael Baum 	return MLX5_TXCMP_CODE_MULTI;
2042256fb0e3SMichael Baum }
2043256fb0e3SMichael Baum 
2044256fb0e3SMichael Baum /**
2045256fb0e3SMichael Baum  * Tx one packet function for multi-segment SEND. Supports all
2046256fb0e3SMichael Baum  * types of Tx offloads, uses MLX5_OPCODE_SEND to build WQEs,
2047256fb0e3SMichael Baum  * sends one packet per WQE, with data inlining in
2048256fb0e3SMichael Baum  * Ethernet Segment and minimal Data Segments.
2049256fb0e3SMichael Baum  *
2050256fb0e3SMichael Baum  * This routine is responsible for storing processed mbuf
2051256fb0e3SMichael Baum  * into elts ring buffer and update elts_head.
2052256fb0e3SMichael Baum  *
2053256fb0e3SMichael Baum  * @param txq
2054256fb0e3SMichael Baum  *   Pointer to TX queue structure.
2055256fb0e3SMichael Baum  * @param loc
2056256fb0e3SMichael Baum  *   Pointer to burst routine local context.
2057256fb0e3SMichael Baum  * @param olx
2058256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
2059256fb0e3SMichael Baum  *   compile time and may be used for optimization.
2060256fb0e3SMichael Baum  *
2061256fb0e3SMichael Baum  * @return
2062256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
2063256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
2064256fb0e3SMichael Baum  * Local context variables partially updated.
2065256fb0e3SMichael Baum  */
2066256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
2067256fb0e3SMichael Baum mlx5_tx_packet_multi_inline(struct mlx5_txq_data *__rte_restrict txq,
2068256fb0e3SMichael Baum 			    struct mlx5_txq_local *__rte_restrict loc,
2069256fb0e3SMichael Baum 			    unsigned int olx)
2070256fb0e3SMichael Baum {
2071256fb0e3SMichael Baum 	struct mlx5_wqe *__rte_restrict wqe;
2072256fb0e3SMichael Baum 	unsigned int ds, inlen, dlen, vlan = 0;
2073256fb0e3SMichael Baum 
2074256fb0e3SMichael Baum 	MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
2075256fb0e3SMichael Baum 	MLX5_ASSERT(NB_SEGS(loc->mbuf) > 1);
207637d6fc30SViacheslav Ovsiienko 	MLX5_ASSERT(loc->elts_free >= NB_SEGS(loc->mbuf));
2077256fb0e3SMichael Baum 	/*
2078256fb0e3SMichael Baum 	 * First calculate data length to be inlined
2079256fb0e3SMichael Baum 	 * to estimate the required space for WQE.
2080256fb0e3SMichael Baum 	 */
2081256fb0e3SMichael Baum 	dlen = rte_pktmbuf_pkt_len(loc->mbuf);
2082daa02b5cSOlivier Matz 	if (MLX5_TXOFF_CONFIG(VLAN) && loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN)
2083256fb0e3SMichael Baum 		vlan = sizeof(struct rte_vlan_hdr);
2084256fb0e3SMichael Baum 	inlen = dlen + vlan;
2085256fb0e3SMichael Baum 	/* Check against minimal length. */
2086256fb0e3SMichael Baum 	if (inlen <= MLX5_ESEG_MIN_INLINE_SIZE)
2087256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_ERROR;
2088256fb0e3SMichael Baum 	MLX5_ASSERT(txq->inlen_send >= MLX5_ESEG_MIN_INLINE_SIZE);
2089256fb0e3SMichael Baum 	if (inlen > txq->inlen_send ||
2090daa02b5cSOlivier Matz 	    loc->mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE) {
2091256fb0e3SMichael Baum 		struct rte_mbuf *mbuf;
2092256fb0e3SMichael Baum 		unsigned int nxlen;
2093256fb0e3SMichael Baum 		uintptr_t start;
2094256fb0e3SMichael Baum 
2095ec837ad0SViacheslav Ovsiienko 		mbuf = loc->mbuf;
2096e3c7bb56SViacheslav Ovsiienko 		nxlen = rte_pktmbuf_data_len(mbuf) + vlan;
2097256fb0e3SMichael Baum 		/*
2098256fb0e3SMichael Baum 		 * Packet length exceeds the allowed inline data length,
2099256fb0e3SMichael Baum 		 * check whether the minimal inlining is required.
2100256fb0e3SMichael Baum 		 */
2101256fb0e3SMichael Baum 		if (txq->inlen_mode) {
2102256fb0e3SMichael Baum 			MLX5_ASSERT(txq->inlen_mode >=
2103256fb0e3SMichael Baum 				    MLX5_ESEG_MIN_INLINE_SIZE);
2104256fb0e3SMichael Baum 			MLX5_ASSERT(txq->inlen_mode <= txq->inlen_send);
21057775172cSDariusz Sosnowski 			inlen = RTE_MIN(txq->inlen_mode, inlen);
2106ec837ad0SViacheslav Ovsiienko 		} else if (vlan && !txq->vlan_en) {
2107256fb0e3SMichael Baum 			/*
2108ec837ad0SViacheslav Ovsiienko 			 * VLAN insertion is requested and hardware does not
2109ec837ad0SViacheslav Ovsiienko 			 * support the offload, will do with software inline.
2110256fb0e3SMichael Baum 			 */
2111256fb0e3SMichael Baum 			inlen = MLX5_ESEG_MIN_INLINE_SIZE;
2112daa02b5cSOlivier Matz 		} else if (mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE ||
2113ec837ad0SViacheslav Ovsiienko 			   nxlen > txq->inlen_send) {
2114ec837ad0SViacheslav Ovsiienko 			return mlx5_tx_packet_multi_send(txq, loc, olx);
2115da4470cbSAlexander Kozyrev 		} else if (nxlen <= MLX5_ESEG_MIN_INLINE_SIZE) {
2116da4470cbSAlexander Kozyrev 			inlen = MLX5_ESEG_MIN_INLINE_SIZE;
2117ec837ad0SViacheslav Ovsiienko 		} else {
2118ec837ad0SViacheslav Ovsiienko 			goto do_first;
2119256fb0e3SMichael Baum 		}
21207775172cSDariusz Sosnowski 		if (mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE)
21217775172cSDariusz Sosnowski 			goto do_build;
2122256fb0e3SMichael Baum 		/*
2123256fb0e3SMichael Baum 		 * Now we know the minimal amount of data is requested
2124256fb0e3SMichael Baum 		 * to inline. Check whether we should inline the buffers
2125256fb0e3SMichael Baum 		 * from the chain beginning to eliminate some mbufs.
2126256fb0e3SMichael Baum 		 */
2127256fb0e3SMichael Baum 		if (unlikely(nxlen <= txq->inlen_send)) {
2128256fb0e3SMichael Baum 			/* We can inline first mbuf at least. */
2129256fb0e3SMichael Baum 			if (nxlen < inlen) {
2130256fb0e3SMichael Baum 				unsigned int smlen;
2131256fb0e3SMichael Baum 
2132256fb0e3SMichael Baum 				/* Scan mbufs till inlen filled. */
2133256fb0e3SMichael Baum 				do {
2134256fb0e3SMichael Baum 					smlen = nxlen;
2135256fb0e3SMichael Baum 					mbuf = NEXT(mbuf);
2136256fb0e3SMichael Baum 					MLX5_ASSERT(mbuf);
2137256fb0e3SMichael Baum 					nxlen = rte_pktmbuf_data_len(mbuf);
2138256fb0e3SMichael Baum 					nxlen += smlen;
2139256fb0e3SMichael Baum 				} while (unlikely(nxlen < inlen));
2140256fb0e3SMichael Baum 				if (unlikely(nxlen > txq->inlen_send)) {
2141256fb0e3SMichael Baum 					/* We cannot inline entire mbuf. */
2142256fb0e3SMichael Baum 					smlen = inlen - smlen;
2143256fb0e3SMichael Baum 					start = rte_pktmbuf_mtod_offset
2144256fb0e3SMichael Baum 						    (mbuf, uintptr_t, smlen);
2145256fb0e3SMichael Baum 					goto do_align;
2146256fb0e3SMichael Baum 				}
2147256fb0e3SMichael Baum 			}
2148ec837ad0SViacheslav Ovsiienko do_first:
2149256fb0e3SMichael Baum 			do {
2150256fb0e3SMichael Baum 				inlen = nxlen;
2151256fb0e3SMichael Baum 				mbuf = NEXT(mbuf);
2152256fb0e3SMichael Baum 				/* There should be not end of packet. */
2153256fb0e3SMichael Baum 				MLX5_ASSERT(mbuf);
21547775172cSDariusz Sosnowski 				if (mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE)
21557775172cSDariusz Sosnowski 					break;
2156256fb0e3SMichael Baum 				nxlen = inlen + rte_pktmbuf_data_len(mbuf);
2157256fb0e3SMichael Baum 			} while (unlikely(nxlen < txq->inlen_send));
2158256fb0e3SMichael Baum 		}
2159256fb0e3SMichael Baum 		start = rte_pktmbuf_mtod(mbuf, uintptr_t);
2160256fb0e3SMichael Baum 		/*
2161256fb0e3SMichael Baum 		 * Check whether we can do inline to align start
2162256fb0e3SMichael Baum 		 * address of data buffer to cacheline.
2163256fb0e3SMichael Baum 		 */
2164256fb0e3SMichael Baum do_align:
2165256fb0e3SMichael Baum 		start = (~start + 1) & (RTE_CACHE_LINE_SIZE - 1);
2166256fb0e3SMichael Baum 		if (unlikely(start)) {
2167256fb0e3SMichael Baum 			start += inlen;
2168256fb0e3SMichael Baum 			if (start <= txq->inlen_send)
2169256fb0e3SMichael Baum 				inlen = start;
2170256fb0e3SMichael Baum 		}
2171256fb0e3SMichael Baum 	}
2172256fb0e3SMichael Baum 	/*
2173256fb0e3SMichael Baum 	 * Check whether there are enough free WQEBBs:
2174256fb0e3SMichael Baum 	 * - Control Segment
2175256fb0e3SMichael Baum 	 * - Ethernet Segment
2176256fb0e3SMichael Baum 	 * - First Segment of inlined Ethernet data
2177256fb0e3SMichael Baum 	 * - ... data continued ...
2178256fb0e3SMichael Baum 	 * - Data Segments of pointer/min inline type
2179256fb0e3SMichael Baum 	 *
2180256fb0e3SMichael Baum 	 * Estimate the number of Data Segments conservatively,
2181256fb0e3SMichael Baum 	 * supposing no any mbufs is being freed during inlining.
2182256fb0e3SMichael Baum 	 */
21837775172cSDariusz Sosnowski do_build:
218437d6fc30SViacheslav Ovsiienko 	if (MLX5_TXOFF_CONFIG(TXPP)) {
218537d6fc30SViacheslav Ovsiienko 		enum mlx5_txcmp_code wret;
218637d6fc30SViacheslav Ovsiienko 
218737d6fc30SViacheslav Ovsiienko 		/* Generate WAIT for scheduling if requested. */
218837d6fc30SViacheslav Ovsiienko 		wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
218937d6fc30SViacheslav Ovsiienko 		if (wret == MLX5_TXCMP_CODE_EXIT)
219037d6fc30SViacheslav Ovsiienko 			return MLX5_TXCMP_CODE_EXIT;
219137d6fc30SViacheslav Ovsiienko 		if (wret == MLX5_TXCMP_CODE_ERROR)
219237d6fc30SViacheslav Ovsiienko 			return MLX5_TXCMP_CODE_ERROR;
219337d6fc30SViacheslav Ovsiienko 	}
2194256fb0e3SMichael Baum 	MLX5_ASSERT(inlen <= txq->inlen_send);
2195256fb0e3SMichael Baum 	ds = NB_SEGS(loc->mbuf) + 2 + (inlen -
2196256fb0e3SMichael Baum 				       MLX5_ESEG_MIN_INLINE_SIZE +
2197256fb0e3SMichael Baum 				       MLX5_WSEG_SIZE +
2198256fb0e3SMichael Baum 				       MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
2199256fb0e3SMichael Baum 	if (unlikely(loc->wqe_free < ((ds + 3) / 4)))
2200256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_EXIT;
2201256fb0e3SMichael Baum 	/* Check for maximal WQE size. */
2202d15bfd29SViacheslav Ovsiienko 	if (unlikely((MLX5_WQE_SIZE_MAX / MLX5_WSEG_SIZE) < ds)) {
2203d15bfd29SViacheslav Ovsiienko 		/*  Check if we can adjust the inline length. */
2204d15bfd29SViacheslav Ovsiienko 		if (unlikely(txq->inlen_mode)) {
2205d15bfd29SViacheslav Ovsiienko 			ds = NB_SEGS(loc->mbuf) + 2 +
2206d15bfd29SViacheslav Ovsiienko 				(txq->inlen_mode -
2207d15bfd29SViacheslav Ovsiienko 				MLX5_ESEG_MIN_INLINE_SIZE +
2208d15bfd29SViacheslav Ovsiienko 				MLX5_WSEG_SIZE +
2209d15bfd29SViacheslav Ovsiienko 				MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
2210e2259f93SDmitry Kozlyuk 			if (unlikely((MLX5_WQE_SIZE_MAX / MLX5_WSEG_SIZE) < ds))
2211256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_ERROR;
2212d15bfd29SViacheslav Ovsiienko 		}
2213d15bfd29SViacheslav Ovsiienko 		/* We have lucky opportunity to adjust. */
2214d15bfd29SViacheslav Ovsiienko 		inlen = RTE_MIN(inlen, MLX5_WQE_SIZE_MAX -
2215d15bfd29SViacheslav Ovsiienko 				       MLX5_WSEG_SIZE * 2 -
2216d15bfd29SViacheslav Ovsiienko 				       MLX5_WSEG_SIZE * NB_SEGS(loc->mbuf) -
2217d15bfd29SViacheslav Ovsiienko 				       MLX5_WSEG_SIZE +
2218d15bfd29SViacheslav Ovsiienko 				       MLX5_ESEG_MIN_INLINE_SIZE);
2219d15bfd29SViacheslav Ovsiienko 	}
2220256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
2221256fb0e3SMichael Baum 	/* Update sent data bytes/packets counters. */
2222256fb0e3SMichael Baum 	txq->stats.obytes += dlen + vlan;
2223256fb0e3SMichael Baum #endif
2224256fb0e3SMichael Baum 	wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
2225256fb0e3SMichael Baum 	loc->wqe_last = wqe;
2226256fb0e3SMichael Baum 	mlx5_tx_cseg_init(txq, loc, wqe, 0, MLX5_OPCODE_SEND, olx);
2227a1e910f5SViacheslav Ovsiienko 	rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
2228256fb0e3SMichael Baum 	ds = mlx5_tx_mseg_build(txq, loc, wqe, vlan, inlen, 0, olx);
2229256fb0e3SMichael Baum 	wqe->cseg.sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | ds);
2230256fb0e3SMichael Baum 	txq->wqe_ci += (ds + 3) / 4;
2231256fb0e3SMichael Baum 	loc->wqe_free -= (ds + 3) / 4;
2232256fb0e3SMichael Baum 	return MLX5_TXCMP_CODE_MULTI;
2233256fb0e3SMichael Baum }
2234256fb0e3SMichael Baum 
2235256fb0e3SMichael Baum /**
2236256fb0e3SMichael Baum  * Tx burst function for multi-segment packets. Supports all
2237256fb0e3SMichael Baum  * types of Tx offloads, uses MLX5_OPCODE_SEND/TSO to build WQEs,
2238256fb0e3SMichael Baum  * sends one packet per WQE. Function stops sending if it
2239256fb0e3SMichael Baum  * encounters the single-segment packet.
2240256fb0e3SMichael Baum  *
2241256fb0e3SMichael Baum  * This routine is responsible for storing processed mbuf
2242256fb0e3SMichael Baum  * into elts ring buffer and update elts_head.
2243256fb0e3SMichael Baum  *
2244256fb0e3SMichael Baum  * @param txq
2245256fb0e3SMichael Baum  *   Pointer to TX queue structure.
2246256fb0e3SMichael Baum  * @param[in] pkts
2247256fb0e3SMichael Baum  *   Packets to transmit.
2248256fb0e3SMichael Baum  * @param pkts_n
2249256fb0e3SMichael Baum  *   Number of packets in array.
2250256fb0e3SMichael Baum  * @param loc
2251256fb0e3SMichael Baum  *   Pointer to burst routine local context.
2252256fb0e3SMichael Baum  * @param olx
2253256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
2254256fb0e3SMichael Baum  *   compile time and may be used for optimization.
2255256fb0e3SMichael Baum  *
2256256fb0e3SMichael Baum  * @return
2257256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
2258256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
2259256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_SINGLE - single-segment packet encountered.
2260256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_TSO - TSO single-segment packet encountered.
2261256fb0e3SMichael Baum  * Local context variables updated.
2262256fb0e3SMichael Baum  */
2263256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
2264256fb0e3SMichael Baum mlx5_tx_burst_mseg(struct mlx5_txq_data *__rte_restrict txq,
2265256fb0e3SMichael Baum 		   struct rte_mbuf **__rte_restrict pkts,
2266256fb0e3SMichael Baum 		   unsigned int pkts_n,
2267256fb0e3SMichael Baum 		   struct mlx5_txq_local *__rte_restrict loc,
2268256fb0e3SMichael Baum 		   unsigned int olx)
2269256fb0e3SMichael Baum {
2270256fb0e3SMichael Baum 	MLX5_ASSERT(loc->elts_free && loc->wqe_free);
2271256fb0e3SMichael Baum 	MLX5_ASSERT(pkts_n > loc->pkts_sent);
2272256fb0e3SMichael Baum 	pkts += loc->pkts_sent + 1;
2273256fb0e3SMichael Baum 	pkts_n -= loc->pkts_sent;
2274256fb0e3SMichael Baum 	for (;;) {
2275256fb0e3SMichael Baum 		enum mlx5_txcmp_code ret;
2276256fb0e3SMichael Baum 
2277256fb0e3SMichael Baum 		MLX5_ASSERT(NB_SEGS(loc->mbuf) > 1);
2278256fb0e3SMichael Baum 		/*
2279256fb0e3SMichael Baum 		 * Estimate the number of free elts quickly but conservatively.
2280256fb0e3SMichael Baum 		 * Some segment may be fully inlined and freed,
2281256fb0e3SMichael Baum 		 * ignore this here - precise estimation is costly.
2282256fb0e3SMichael Baum 		 */
2283256fb0e3SMichael Baum 		if (loc->elts_free < NB_SEGS(loc->mbuf))
2284256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
2285256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(TSO) &&
2286daa02b5cSOlivier Matz 		    unlikely(loc->mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG)) {
2287256fb0e3SMichael Baum 			/* Proceed with multi-segment TSO. */
2288256fb0e3SMichael Baum 			ret = mlx5_tx_packet_multi_tso(txq, loc, olx);
2289256fb0e3SMichael Baum 		} else if (MLX5_TXOFF_CONFIG(INLINE)) {
2290256fb0e3SMichael Baum 			/* Proceed with multi-segment SEND with inlining. */
2291256fb0e3SMichael Baum 			ret = mlx5_tx_packet_multi_inline(txq, loc, olx);
2292256fb0e3SMichael Baum 		} else {
2293256fb0e3SMichael Baum 			/* Proceed with multi-segment SEND w/o inlining. */
2294256fb0e3SMichael Baum 			ret = mlx5_tx_packet_multi_send(txq, loc, olx);
2295256fb0e3SMichael Baum 		}
2296256fb0e3SMichael Baum 		if (ret == MLX5_TXCMP_CODE_EXIT)
2297256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
2298256fb0e3SMichael Baum 		if (ret == MLX5_TXCMP_CODE_ERROR)
2299256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_ERROR;
2300256fb0e3SMichael Baum 		/* WQE is built, go to the next packet. */
2301256fb0e3SMichael Baum 		++loc->pkts_sent;
2302256fb0e3SMichael Baum 		--pkts_n;
2303256fb0e3SMichael Baum 		if (unlikely(!pkts_n || !loc->elts_free || !loc->wqe_free))
2304256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
2305256fb0e3SMichael Baum 		loc->mbuf = *pkts++;
2306256fb0e3SMichael Baum 		if (pkts_n > 1)
2307256fb0e3SMichael Baum 			rte_prefetch0(*pkts);
2308256fb0e3SMichael Baum 		if (likely(NB_SEGS(loc->mbuf) > 1))
2309256fb0e3SMichael Baum 			continue;
2310256fb0e3SMichael Baum 		/* Here ends the series of multi-segment packets. */
2311256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(TSO) &&
2312daa02b5cSOlivier Matz 		    unlikely(loc->mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG))
2313256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_TSO;
2314256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_SINGLE;
2315256fb0e3SMichael Baum 	}
2316256fb0e3SMichael Baum 	MLX5_ASSERT(false);
2317256fb0e3SMichael Baum }
2318256fb0e3SMichael Baum 
2319256fb0e3SMichael Baum /**
2320256fb0e3SMichael Baum  * Tx burst function for single-segment packets with TSO.
2321256fb0e3SMichael Baum  * Supports all types of Tx offloads, except multi-packets.
2322256fb0e3SMichael Baum  * Uses MLX5_OPCODE_TSO to build WQEs, sends one packet per WQE.
2323256fb0e3SMichael Baum  * Function stops sending if it encounters the multi-segment
2324256fb0e3SMichael Baum  * packet or packet without TSO requested.
2325256fb0e3SMichael Baum  *
2326256fb0e3SMichael Baum  * The routine is responsible for storing processed mbuf into elts ring buffer
2327256fb0e3SMichael Baum  * and update elts_head if inline offloads is requested due to possible early
2328256fb0e3SMichael Baum  * freeing of the inlined mbufs (can not store pkts array in elts as a batch).
2329256fb0e3SMichael Baum  *
2330256fb0e3SMichael Baum  * @param txq
2331256fb0e3SMichael Baum  *   Pointer to TX queue structure.
2332256fb0e3SMichael Baum  * @param[in] pkts
2333256fb0e3SMichael Baum  *   Packets to transmit.
2334256fb0e3SMichael Baum  * @param pkts_n
2335256fb0e3SMichael Baum  *   Number of packets in array.
2336256fb0e3SMichael Baum  * @param loc
2337256fb0e3SMichael Baum  *   Pointer to burst routine local context.
2338256fb0e3SMichael Baum  * @param olx
2339256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
2340256fb0e3SMichael Baum  *   compile time and may be used for optimization.
2341256fb0e3SMichael Baum  *
2342256fb0e3SMichael Baum  * @return
2343256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
2344256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
2345256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_SINGLE - single-segment packet encountered.
2346256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_MULTI - multi-segment packet encountered.
2347256fb0e3SMichael Baum  * Local context variables updated.
2348256fb0e3SMichael Baum  */
2349256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
2350256fb0e3SMichael Baum mlx5_tx_burst_tso(struct mlx5_txq_data *__rte_restrict txq,
2351256fb0e3SMichael Baum 		  struct rte_mbuf **__rte_restrict pkts,
2352256fb0e3SMichael Baum 		  unsigned int pkts_n,
2353256fb0e3SMichael Baum 		  struct mlx5_txq_local *__rte_restrict loc,
2354256fb0e3SMichael Baum 		  unsigned int olx)
2355256fb0e3SMichael Baum {
2356256fb0e3SMichael Baum 	MLX5_ASSERT(loc->elts_free && loc->wqe_free);
2357256fb0e3SMichael Baum 	MLX5_ASSERT(pkts_n > loc->pkts_sent);
2358256fb0e3SMichael Baum 	pkts += loc->pkts_sent + 1;
2359256fb0e3SMichael Baum 	pkts_n -= loc->pkts_sent;
2360256fb0e3SMichael Baum 	for (;;) {
2361256fb0e3SMichael Baum 		struct mlx5_wqe_dseg *__rte_restrict dseg;
2362256fb0e3SMichael Baum 		struct mlx5_wqe *__rte_restrict wqe;
2363256fb0e3SMichael Baum 		unsigned int ds, dlen, hlen, ntcp, vlan = 0;
2364256fb0e3SMichael Baum 		uint8_t *dptr;
2365256fb0e3SMichael Baum 
2366256fb0e3SMichael Baum 		MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
2367256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(TXPP)) {
2368256fb0e3SMichael Baum 			enum mlx5_txcmp_code wret;
2369256fb0e3SMichael Baum 
2370256fb0e3SMichael Baum 			/* Generate WAIT for scheduling if requested. */
237137d6fc30SViacheslav Ovsiienko 			wret = mlx5_tx_schedule_send(txq, loc, 1, olx);
2372256fb0e3SMichael Baum 			if (wret == MLX5_TXCMP_CODE_EXIT)
2373256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_EXIT;
2374256fb0e3SMichael Baum 			if (wret == MLX5_TXCMP_CODE_ERROR)
2375256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_ERROR;
2376256fb0e3SMichael Baum 		}
2377256fb0e3SMichael Baum 		dlen = rte_pktmbuf_data_len(loc->mbuf);
2378256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(VLAN) &&
2379daa02b5cSOlivier Matz 		    loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) {
2380256fb0e3SMichael Baum 			vlan = sizeof(struct rte_vlan_hdr);
2381256fb0e3SMichael Baum 		}
2382256fb0e3SMichael Baum 		/*
2383256fb0e3SMichael Baum 		 * First calculate the WQE size to check
2384256fb0e3SMichael Baum 		 * whether we have enough space in ring buffer.
2385256fb0e3SMichael Baum 		 */
2386256fb0e3SMichael Baum 		hlen = loc->mbuf->l2_len + vlan +
2387256fb0e3SMichael Baum 		       loc->mbuf->l3_len + loc->mbuf->l4_len;
2388256fb0e3SMichael Baum 		if (unlikely((!hlen || !loc->mbuf->tso_segsz)))
2389256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_ERROR;
2390daa02b5cSOlivier Matz 		if (loc->mbuf->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK)
2391256fb0e3SMichael Baum 			hlen += loc->mbuf->outer_l2_len +
2392256fb0e3SMichael Baum 				loc->mbuf->outer_l3_len;
2393256fb0e3SMichael Baum 		/* Segment must contain all TSO headers. */
2394256fb0e3SMichael Baum 		if (unlikely(hlen > MLX5_MAX_TSO_HEADER ||
2395256fb0e3SMichael Baum 			     hlen <= MLX5_ESEG_MIN_INLINE_SIZE ||
2396256fb0e3SMichael Baum 			     hlen > (dlen + vlan)))
2397256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_ERROR;
2398256fb0e3SMichael Baum 		/*
2399256fb0e3SMichael Baum 		 * Check whether there are enough free WQEBBs:
2400256fb0e3SMichael Baum 		 * - Control Segment
2401256fb0e3SMichael Baum 		 * - Ethernet Segment
2402256fb0e3SMichael Baum 		 * - First Segment of inlined Ethernet data
2403256fb0e3SMichael Baum 		 * - ... data continued ...
2404256fb0e3SMichael Baum 		 * - Finishing Data Segment of pointer type
2405256fb0e3SMichael Baum 		 */
2406256fb0e3SMichael Baum 		ds = 4 + (hlen - MLX5_ESEG_MIN_INLINE_SIZE +
2407256fb0e3SMichael Baum 			  MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
2408256fb0e3SMichael Baum 		if (loc->wqe_free < ((ds + 3) / 4))
2409256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
2410256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
2411256fb0e3SMichael Baum 		/* Update sent data bytes/packets counters. */
2412256fb0e3SMichael Baum 		ntcp = (dlen + vlan - hlen +
2413256fb0e3SMichael Baum 			loc->mbuf->tso_segsz - 1) /
2414256fb0e3SMichael Baum 			loc->mbuf->tso_segsz;
2415256fb0e3SMichael Baum 		/*
2416256fb0e3SMichael Baum 		 * One will be added for mbuf itself at the end
2417256fb0e3SMichael Baum 		 * of the mlx5_tx_burst from loc->pkts_sent field.
2418256fb0e3SMichael Baum 		 */
2419256fb0e3SMichael Baum 		--ntcp;
2420256fb0e3SMichael Baum 		txq->stats.opackets += ntcp;
2421256fb0e3SMichael Baum 		txq->stats.obytes += dlen + vlan + ntcp * hlen;
2422256fb0e3SMichael Baum #endif
2423256fb0e3SMichael Baum 		/*
2424256fb0e3SMichael Baum 		 * Build the TSO WQE:
2425256fb0e3SMichael Baum 		 * - Control Segment
2426256fb0e3SMichael Baum 		 * - Ethernet Segment with hlen bytes inlined
2427256fb0e3SMichael Baum 		 * - Data Segment of pointer type
2428256fb0e3SMichael Baum 		 */
2429256fb0e3SMichael Baum 		wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
2430256fb0e3SMichael Baum 		loc->wqe_last = wqe;
2431a1e910f5SViacheslav Ovsiienko 		mlx5_tx_cseg_init(txq, loc, wqe, ds, MLX5_OPCODE_TSO, olx);
2432a1e910f5SViacheslav Ovsiienko 		rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
2433256fb0e3SMichael Baum 		dseg = mlx5_tx_eseg_data(txq, loc, wqe, vlan, hlen, 1, olx);
2434256fb0e3SMichael Baum 		dptr = rte_pktmbuf_mtod(loc->mbuf, uint8_t *) + hlen - vlan;
2435256fb0e3SMichael Baum 		dlen -= hlen - vlan;
2436256fb0e3SMichael Baum 		mlx5_tx_dseg_ptr(txq, loc, dseg, dptr, dlen, olx);
2437256fb0e3SMichael Baum 		/*
2438256fb0e3SMichael Baum 		 * WQE is built, update the loop parameters
2439256fb0e3SMichael Baum 		 * and go to the next packet.
2440256fb0e3SMichael Baum 		 */
2441256fb0e3SMichael Baum 		txq->wqe_ci += (ds + 3) / 4;
2442256fb0e3SMichael Baum 		loc->wqe_free -= (ds + 3) / 4;
2443256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(INLINE))
2444256fb0e3SMichael Baum 			txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
2445256fb0e3SMichael Baum 		--loc->elts_free;
2446256fb0e3SMichael Baum 		++loc->pkts_sent;
2447256fb0e3SMichael Baum 		--pkts_n;
2448256fb0e3SMichael Baum 		if (unlikely(!pkts_n || !loc->elts_free || !loc->wqe_free))
2449256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
2450256fb0e3SMichael Baum 		loc->mbuf = *pkts++;
2451256fb0e3SMichael Baum 		if (pkts_n > 1)
2452256fb0e3SMichael Baum 			rte_prefetch0(*pkts);
2453256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(MULTI) &&
2454256fb0e3SMichael Baum 		    unlikely(NB_SEGS(loc->mbuf) > 1))
2455256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_MULTI;
2456daa02b5cSOlivier Matz 		if (likely(!(loc->mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG)))
2457256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_SINGLE;
2458256fb0e3SMichael Baum 		/* Continue with the next TSO packet. */
2459256fb0e3SMichael Baum 	}
2460256fb0e3SMichael Baum 	MLX5_ASSERT(false);
2461256fb0e3SMichael Baum }
2462256fb0e3SMichael Baum 
2463256fb0e3SMichael Baum /**
2464256fb0e3SMichael Baum  * Analyze the packet and select the best method to send.
2465256fb0e3SMichael Baum  *
2466256fb0e3SMichael Baum  * @param txq
2467256fb0e3SMichael Baum  *   Pointer to TX queue structure.
2468256fb0e3SMichael Baum  * @param loc
2469256fb0e3SMichael Baum  *   Pointer to burst routine local context.
2470256fb0e3SMichael Baum  * @param olx
2471256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
2472256fb0e3SMichael Baum  *   compile time and may be used for optimization.
2473256fb0e3SMichael Baum  * @param newp
2474256fb0e3SMichael Baum  *   The predefined flag whether do complete check for
2475256fb0e3SMichael Baum  *   multi-segment packets and TSO.
2476256fb0e3SMichael Baum  *
2477256fb0e3SMichael Baum  * @return
2478256fb0e3SMichael Baum  *  MLX5_TXCMP_CODE_MULTI - multi-segment packet encountered.
2479256fb0e3SMichael Baum  *  MLX5_TXCMP_CODE_TSO - TSO required, use TSO/LSO.
2480256fb0e3SMichael Baum  *  MLX5_TXCMP_CODE_SINGLE - single-segment packet, use SEND.
2481256fb0e3SMichael Baum  *  MLX5_TXCMP_CODE_EMPW - single-segment packet, use MPW.
2482256fb0e3SMichael Baum  */
2483256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
2484256fb0e3SMichael Baum mlx5_tx_able_to_empw(struct mlx5_txq_data *__rte_restrict txq,
2485256fb0e3SMichael Baum 		     struct mlx5_txq_local *__rte_restrict loc,
2486256fb0e3SMichael Baum 		     unsigned int olx,
2487256fb0e3SMichael Baum 		     bool newp)
2488256fb0e3SMichael Baum {
2489256fb0e3SMichael Baum 	/* Check for multi-segment packet. */
2490256fb0e3SMichael Baum 	if (newp &&
2491256fb0e3SMichael Baum 	    MLX5_TXOFF_CONFIG(MULTI) &&
2492256fb0e3SMichael Baum 	    unlikely(NB_SEGS(loc->mbuf) > 1))
2493256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_MULTI;
2494256fb0e3SMichael Baum 	/* Check for TSO packet. */
2495256fb0e3SMichael Baum 	if (newp &&
2496256fb0e3SMichael Baum 	    MLX5_TXOFF_CONFIG(TSO) &&
2497daa02b5cSOlivier Matz 	    unlikely(loc->mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG))
2498256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_TSO;
2499256fb0e3SMichael Baum 	/* Check if eMPW is enabled at all. */
2500256fb0e3SMichael Baum 	if (!MLX5_TXOFF_CONFIG(EMPW))
2501256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_SINGLE;
2502256fb0e3SMichael Baum 	/* Check if eMPW can be engaged. */
2503256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(VLAN) &&
2504daa02b5cSOlivier Matz 	    unlikely(loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) &&
2505256fb0e3SMichael Baum 		(!MLX5_TXOFF_CONFIG(INLINE) ||
2506256fb0e3SMichael Baum 		 unlikely((rte_pktmbuf_data_len(loc->mbuf) +
2507256fb0e3SMichael Baum 			   sizeof(struct rte_vlan_hdr)) > txq->inlen_empw))) {
2508256fb0e3SMichael Baum 		/*
2509256fb0e3SMichael Baum 		 * eMPW does not support VLAN insertion offload, we have to
2510256fb0e3SMichael Baum 		 * inline the entire packet but packet is too long for inlining.
2511256fb0e3SMichael Baum 		 */
2512256fb0e3SMichael Baum 		return MLX5_TXCMP_CODE_SINGLE;
2513256fb0e3SMichael Baum 	}
2514256fb0e3SMichael Baum 	return MLX5_TXCMP_CODE_EMPW;
2515256fb0e3SMichael Baum }
2516256fb0e3SMichael Baum 
2517256fb0e3SMichael Baum /**
2518256fb0e3SMichael Baum  * Check the next packet attributes to match with the eMPW batch ones.
2519256fb0e3SMichael Baum  * In addition, for legacy MPW the packet length is checked either.
2520256fb0e3SMichael Baum  *
2521256fb0e3SMichael Baum  * @param txq
2522256fb0e3SMichael Baum  *   Pointer to TX queue structure.
2523256fb0e3SMichael Baum  * @param es
2524256fb0e3SMichael Baum  *   Pointer to Ethernet Segment of eMPW batch.
2525256fb0e3SMichael Baum  * @param loc
2526256fb0e3SMichael Baum  *   Pointer to burst routine local context.
2527256fb0e3SMichael Baum  * @param dlen
2528256fb0e3SMichael Baum  *   Length of previous packet in MPW descriptor.
2529256fb0e3SMichael Baum  * @param olx
2530256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
2531256fb0e3SMichael Baum  *   compile time and may be used for optimization.
2532256fb0e3SMichael Baum  *
2533256fb0e3SMichael Baum  * @return
2534256fb0e3SMichael Baum  *  true - packet match with eMPW batch attributes.
2535256fb0e3SMichael Baum  *  false - no match, eMPW should be restarted.
2536256fb0e3SMichael Baum  */
2537256fb0e3SMichael Baum static __rte_always_inline bool
2538256fb0e3SMichael Baum mlx5_tx_match_empw(struct mlx5_txq_data *__rte_restrict txq,
2539256fb0e3SMichael Baum 		   struct mlx5_wqe_eseg *__rte_restrict es,
2540256fb0e3SMichael Baum 		   struct mlx5_txq_local *__rte_restrict loc,
2541256fb0e3SMichael Baum 		   uint32_t dlen,
2542256fb0e3SMichael Baum 		   unsigned int olx)
2543256fb0e3SMichael Baum {
2544256fb0e3SMichael Baum 	uint8_t swp_flags = 0;
2545256fb0e3SMichael Baum 
2546256fb0e3SMichael Baum 	/* Compare the checksum flags, if any. */
2547256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(CSUM) &&
2548256fb0e3SMichael Baum 	    txq_ol_cksum_to_cs(loc->mbuf) != es->cs_flags)
2549256fb0e3SMichael Baum 		return false;
2550256fb0e3SMichael Baum 	/* Compare the Software Parser offsets and flags. */
2551256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(SWP) &&
2552256fb0e3SMichael Baum 	    (es->swp_offs != txq_mbuf_to_swp(loc, &swp_flags, olx) ||
2553256fb0e3SMichael Baum 	     es->swp_flags != swp_flags))
2554256fb0e3SMichael Baum 		return false;
2555256fb0e3SMichael Baum 	/* Fill metadata field if needed. */
2556256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(METADATA) &&
2557daa02b5cSOlivier Matz 		es->metadata != (loc->mbuf->ol_flags & RTE_MBUF_DYNFLAG_TX_METADATA ?
25586728fe93SBing Zhao 				 rte_cpu_to_be_32(*RTE_FLOW_DYNF_METADATA(loc->mbuf)) : 0))
2559256fb0e3SMichael Baum 		return false;
2560256fb0e3SMichael Baum 	/* Legacy MPW can send packets with the same length only. */
2561256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(MPW) &&
2562256fb0e3SMichael Baum 	    dlen != rte_pktmbuf_data_len(loc->mbuf))
2563256fb0e3SMichael Baum 		return false;
2564256fb0e3SMichael Baum 	/* There must be no VLAN packets in eMPW loop. */
2565256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(VLAN))
2566daa02b5cSOlivier Matz 		MLX5_ASSERT(!(loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN));
2567256fb0e3SMichael Baum 	/* Check if the scheduling is requested. */
2568256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(TXPP) &&
2569256fb0e3SMichael Baum 	    loc->mbuf->ol_flags & txq->ts_mask)
2570256fb0e3SMichael Baum 		return false;
2571256fb0e3SMichael Baum 	return true;
2572256fb0e3SMichael Baum }
2573256fb0e3SMichael Baum 
2574256fb0e3SMichael Baum /**
2575256fb0e3SMichael Baum  * Update send loop variables and WQE for eMPW loop without data inlining.
2576256fb0e3SMichael Baum  * Number of Data Segments is equal to the number of sent packets.
2577256fb0e3SMichael Baum  *
2578256fb0e3SMichael Baum  * @param txq
2579256fb0e3SMichael Baum  *   Pointer to TX queue structure.
2580256fb0e3SMichael Baum  * @param loc
2581256fb0e3SMichael Baum  *   Pointer to burst routine local context.
2582256fb0e3SMichael Baum  * @param ds
2583256fb0e3SMichael Baum  *   Number of packets/Data Segments/Packets.
2584256fb0e3SMichael Baum  * @param slen
2585256fb0e3SMichael Baum  *   Accumulated statistics, bytes sent.
2586256fb0e3SMichael Baum  * @param olx
2587256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
2588256fb0e3SMichael Baum  *   compile time and may be used for optimization.
2589256fb0e3SMichael Baum  *
2590256fb0e3SMichael Baum  * @return
2591256fb0e3SMichael Baum  *  true - packet match with eMPW batch attributes.
2592256fb0e3SMichael Baum  *  false - no match, eMPW should be restarted.
2593256fb0e3SMichael Baum  */
2594256fb0e3SMichael Baum static __rte_always_inline void
2595256fb0e3SMichael Baum mlx5_tx_sdone_empw(struct mlx5_txq_data *__rte_restrict txq,
2596256fb0e3SMichael Baum 		   struct mlx5_txq_local *__rte_restrict loc,
2597256fb0e3SMichael Baum 		   unsigned int ds,
2598256fb0e3SMichael Baum 		   unsigned int slen,
2599256fb0e3SMichael Baum 		   unsigned int olx __rte_unused)
2600256fb0e3SMichael Baum {
2601256fb0e3SMichael Baum 	MLX5_ASSERT(!MLX5_TXOFF_CONFIG(INLINE));
2602256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
2603256fb0e3SMichael Baum 	/* Update sent data bytes counter. */
2604256fb0e3SMichael Baum 	 txq->stats.obytes += slen;
2605256fb0e3SMichael Baum #else
2606256fb0e3SMichael Baum 	(void)slen;
2607256fb0e3SMichael Baum #endif
2608256fb0e3SMichael Baum 	loc->elts_free -= ds;
2609256fb0e3SMichael Baum 	loc->pkts_sent += ds;
2610256fb0e3SMichael Baum 	ds += 2;
2611256fb0e3SMichael Baum 	loc->wqe_last->cseg.sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | ds);
2612256fb0e3SMichael Baum 	txq->wqe_ci += (ds + 3) / 4;
2613256fb0e3SMichael Baum 	loc->wqe_free -= (ds + 3) / 4;
2614256fb0e3SMichael Baum }
2615256fb0e3SMichael Baum 
2616256fb0e3SMichael Baum /**
2617256fb0e3SMichael Baum  * Update send loop variables and WQE for eMPW loop with data inlining.
2618256fb0e3SMichael Baum  * Gets the size of pushed descriptors and data to the WQE.
2619256fb0e3SMichael Baum  *
2620256fb0e3SMichael Baum  * @param txq
2621256fb0e3SMichael Baum  *   Pointer to TX queue structure.
2622256fb0e3SMichael Baum  * @param loc
2623256fb0e3SMichael Baum  *   Pointer to burst routine local context.
2624256fb0e3SMichael Baum  * @param len
2625256fb0e3SMichael Baum  *   Total size of descriptor/data in bytes.
2626256fb0e3SMichael Baum  * @param slen
2627256fb0e3SMichael Baum  *   Accumulated statistics, data bytes sent.
2628256fb0e3SMichael Baum  * @param wqem
2629256fb0e3SMichael Baum  *   The base WQE for the eMPW/MPW descriptor.
2630256fb0e3SMichael Baum  * @param olx
2631256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
2632256fb0e3SMichael Baum  *   compile time and may be used for optimization.
2633256fb0e3SMichael Baum  *
2634256fb0e3SMichael Baum  * @return
2635256fb0e3SMichael Baum  *  true - packet match with eMPW batch attributes.
2636256fb0e3SMichael Baum  *  false - no match, eMPW should be restarted.
2637256fb0e3SMichael Baum  */
2638256fb0e3SMichael Baum static __rte_always_inline void
2639256fb0e3SMichael Baum mlx5_tx_idone_empw(struct mlx5_txq_data *__rte_restrict txq,
2640256fb0e3SMichael Baum 		   struct mlx5_txq_local *__rte_restrict loc,
2641256fb0e3SMichael Baum 		   unsigned int len,
2642256fb0e3SMichael Baum 		   unsigned int slen,
2643256fb0e3SMichael Baum 		   struct mlx5_wqe *__rte_restrict wqem,
2644256fb0e3SMichael Baum 		   unsigned int olx __rte_unused)
2645256fb0e3SMichael Baum {
2646256fb0e3SMichael Baum 	struct mlx5_wqe_dseg *dseg = &wqem->dseg[0];
2647256fb0e3SMichael Baum 
2648256fb0e3SMichael Baum 	MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
2649256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
2650256fb0e3SMichael Baum 	/* Update sent data bytes counter. */
2651256fb0e3SMichael Baum 	 txq->stats.obytes += slen;
2652256fb0e3SMichael Baum #else
2653256fb0e3SMichael Baum 	(void)slen;
2654256fb0e3SMichael Baum #endif
2655256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(MPW) && dseg->bcount == RTE_BE32(0)) {
2656256fb0e3SMichael Baum 		/*
2657256fb0e3SMichael Baum 		 * If the legacy MPW session contains the inline packets
2658256fb0e3SMichael Baum 		 * we should set the only inline data segment length
2659256fb0e3SMichael Baum 		 * and align the total length to the segment size.
2660256fb0e3SMichael Baum 		 */
2661256fb0e3SMichael Baum 		MLX5_ASSERT(len > sizeof(dseg->bcount));
2662256fb0e3SMichael Baum 		dseg->bcount = rte_cpu_to_be_32((len - sizeof(dseg->bcount)) |
2663256fb0e3SMichael Baum 						MLX5_ETH_WQE_DATA_INLINE);
2664256fb0e3SMichael Baum 		len = (len + MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE + 2;
2665256fb0e3SMichael Baum 	} else {
2666256fb0e3SMichael Baum 		/*
2667256fb0e3SMichael Baum 		 * The session is not legacy MPW or contains the
2668256fb0e3SMichael Baum 		 * data buffer pointer segments.
2669256fb0e3SMichael Baum 		 */
2670256fb0e3SMichael Baum 		MLX5_ASSERT((len % MLX5_WSEG_SIZE) == 0);
2671256fb0e3SMichael Baum 		len = len / MLX5_WSEG_SIZE + 2;
2672256fb0e3SMichael Baum 	}
2673256fb0e3SMichael Baum 	wqem->cseg.sq_ds = rte_cpu_to_be_32(txq->qp_num_8s | len);
2674256fb0e3SMichael Baum 	txq->wqe_ci += (len + 3) / 4;
2675256fb0e3SMichael Baum 	loc->wqe_free -= (len + 3) / 4;
2676256fb0e3SMichael Baum 	loc->wqe_last = wqem;
2677256fb0e3SMichael Baum }
2678256fb0e3SMichael Baum 
2679256fb0e3SMichael Baum /**
2680256fb0e3SMichael Baum  * The set of Tx burst functions for single-segment packets without TSO
2681256fb0e3SMichael Baum  * and with Multi-Packet Writing feature support.
2682256fb0e3SMichael Baum  * Supports all types of Tx offloads, except multi-packets and TSO.
2683256fb0e3SMichael Baum  *
2684256fb0e3SMichael Baum  * Uses MLX5_OPCODE_EMPW to build WQEs if possible and sends as many packet
2685256fb0e3SMichael Baum  * per WQE as it can. If eMPW is not configured or packet can not be sent with
2686256fb0e3SMichael Baum  * eMPW (VLAN insertion) the ordinary SEND opcode is used and only one packet
2687256fb0e3SMichael Baum  * placed in WQE.
2688256fb0e3SMichael Baum  *
2689256fb0e3SMichael Baum  * Functions stop sending if it encounters the multi-segment packet or packet
2690256fb0e3SMichael Baum  * with TSO requested.
2691256fb0e3SMichael Baum  *
2692256fb0e3SMichael Baum  * The routines are responsible for storing processed mbuf into elts ring buffer
2693256fb0e3SMichael Baum  * and update elts_head if inlining offload is requested. Otherwise the copying
2694256fb0e3SMichael Baum  * mbufs to elts can be postponed and completed at the end of burst routine.
2695256fb0e3SMichael Baum  *
2696256fb0e3SMichael Baum  * @param txq
2697256fb0e3SMichael Baum  *   Pointer to TX queue structure.
2698256fb0e3SMichael Baum  * @param[in] pkts
2699256fb0e3SMichael Baum  *   Packets to transmit.
2700256fb0e3SMichael Baum  * @param pkts_n
2701256fb0e3SMichael Baum  *   Number of packets in array.
2702256fb0e3SMichael Baum  * @param loc
2703256fb0e3SMichael Baum  *   Pointer to burst routine local context.
2704256fb0e3SMichael Baum  * @param olx
2705256fb0e3SMichael Baum  *   Configured Tx offloads mask. It is fully defined at
2706256fb0e3SMichael Baum  *   compile time and may be used for optimization.
2707256fb0e3SMichael Baum  *
2708256fb0e3SMichael Baum  * @return
2709256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_EXIT - sending is done or impossible.
2710256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_ERROR - some unrecoverable error occurred.
2711256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_MULTI - multi-segment packet encountered.
2712256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_TSO - TSO packet encountered.
2713256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_SINGLE - used inside functions set.
2714256fb0e3SMichael Baum  *   MLX5_TXCMP_CODE_EMPW - used inside functions set.
2715256fb0e3SMichael Baum  *
2716256fb0e3SMichael Baum  * Local context variables updated.
2717256fb0e3SMichael Baum  *
2718256fb0e3SMichael Baum  *
2719256fb0e3SMichael Baum  * The routine sends packets with MLX5_OPCODE_EMPW
2720256fb0e3SMichael Baum  * without inlining, this is dedicated optimized branch.
2721256fb0e3SMichael Baum  * No VLAN insertion is supported.
2722256fb0e3SMichael Baum  */
2723256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
2724256fb0e3SMichael Baum mlx5_tx_burst_empw_simple(struct mlx5_txq_data *__rte_restrict txq,
2725256fb0e3SMichael Baum 			  struct rte_mbuf **__rte_restrict pkts,
2726256fb0e3SMichael Baum 			  unsigned int pkts_n,
2727256fb0e3SMichael Baum 			  struct mlx5_txq_local *__rte_restrict loc,
2728256fb0e3SMichael Baum 			  unsigned int olx)
2729256fb0e3SMichael Baum {
2730256fb0e3SMichael Baum 	/*
2731256fb0e3SMichael Baum 	 * Subroutine is the part of mlx5_tx_burst_single() and sends
2732256fb0e3SMichael Baum 	 * single-segment packet with eMPW opcode without data inlining.
2733256fb0e3SMichael Baum 	 */
2734256fb0e3SMichael Baum 	MLX5_ASSERT(!MLX5_TXOFF_CONFIG(INLINE));
2735256fb0e3SMichael Baum 	MLX5_ASSERT(MLX5_TXOFF_CONFIG(EMPW));
2736256fb0e3SMichael Baum 	MLX5_ASSERT(loc->elts_free && loc->wqe_free);
2737256fb0e3SMichael Baum 	MLX5_ASSERT(pkts_n > loc->pkts_sent);
2738256fb0e3SMichael Baum 	pkts += loc->pkts_sent + 1;
2739256fb0e3SMichael Baum 	pkts_n -= loc->pkts_sent;
2740256fb0e3SMichael Baum 	for (;;) {
2741256fb0e3SMichael Baum 		struct mlx5_wqe_dseg *__rte_restrict dseg;
2742256fb0e3SMichael Baum 		struct mlx5_wqe_eseg *__rte_restrict eseg;
2743256fb0e3SMichael Baum 		enum mlx5_txcmp_code ret;
2744256fb0e3SMichael Baum 		unsigned int part, loop;
2745256fb0e3SMichael Baum 		unsigned int slen = 0;
2746256fb0e3SMichael Baum 
2747256fb0e3SMichael Baum next_empw:
2748256fb0e3SMichael Baum 		MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
2749256fb0e3SMichael Baum 		part = RTE_MIN(pkts_n, MLX5_TXOFF_CONFIG(MPW) ?
2750256fb0e3SMichael Baum 				       MLX5_MPW_MAX_PACKETS :
2751256fb0e3SMichael Baum 				       MLX5_EMPW_MAX_PACKETS);
2752256fb0e3SMichael Baum 		if (unlikely(loc->elts_free < part)) {
2753256fb0e3SMichael Baum 			/* We have no enough elts to save all mbufs. */
2754256fb0e3SMichael Baum 			if (unlikely(loc->elts_free < MLX5_EMPW_MIN_PACKETS))
2755256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_EXIT;
2756256fb0e3SMichael Baum 			/* But we still able to send at least minimal eMPW. */
2757256fb0e3SMichael Baum 			part = loc->elts_free;
2758256fb0e3SMichael Baum 		}
275937d6fc30SViacheslav Ovsiienko 		if (MLX5_TXOFF_CONFIG(TXPP)) {
276037d6fc30SViacheslav Ovsiienko 			enum mlx5_txcmp_code wret;
276137d6fc30SViacheslav Ovsiienko 
276237d6fc30SViacheslav Ovsiienko 			/* Generate WAIT for scheduling if requested. */
276337d6fc30SViacheslav Ovsiienko 			wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
276437d6fc30SViacheslav Ovsiienko 			if (wret == MLX5_TXCMP_CODE_EXIT)
276537d6fc30SViacheslav Ovsiienko 				return MLX5_TXCMP_CODE_EXIT;
276637d6fc30SViacheslav Ovsiienko 			if (wret == MLX5_TXCMP_CODE_ERROR)
276737d6fc30SViacheslav Ovsiienko 				return MLX5_TXCMP_CODE_ERROR;
276837d6fc30SViacheslav Ovsiienko 		}
2769256fb0e3SMichael Baum 		/* Check whether we have enough WQEs */
2770256fb0e3SMichael Baum 		if (unlikely(loc->wqe_free < ((2 + part + 3) / 4))) {
2771256fb0e3SMichael Baum 			if (unlikely(loc->wqe_free <
2772256fb0e3SMichael Baum 				((2 + MLX5_EMPW_MIN_PACKETS + 3) / 4)))
2773256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_EXIT;
2774256fb0e3SMichael Baum 			part = (loc->wqe_free * 4) - 2;
2775256fb0e3SMichael Baum 		}
2776256fb0e3SMichael Baum 		if (likely(part > 1))
2777256fb0e3SMichael Baum 			rte_prefetch0(*pkts);
2778256fb0e3SMichael Baum 		loc->wqe_last = txq->wqes + (txq->wqe_ci & txq->wqe_m);
2779256fb0e3SMichael Baum 		/*
2780256fb0e3SMichael Baum 		 * Build eMPW title WQEBB:
2781256fb0e3SMichael Baum 		 * - Control Segment, eMPW opcode
2782256fb0e3SMichael Baum 		 * - Ethernet Segment, no inline
2783256fb0e3SMichael Baum 		 */
2784256fb0e3SMichael Baum 		mlx5_tx_cseg_init(txq, loc, loc->wqe_last, part + 2,
2785256fb0e3SMichael Baum 				  MLX5_OPCODE_ENHANCED_MPSW, olx);
2786256fb0e3SMichael Baum 		mlx5_tx_eseg_none(txq, loc, loc->wqe_last,
2787256fb0e3SMichael Baum 				  olx & ~MLX5_TXOFF_CONFIG_VLAN);
2788256fb0e3SMichael Baum 		eseg = &loc->wqe_last->eseg;
2789256fb0e3SMichael Baum 		dseg = &loc->wqe_last->dseg[0];
2790256fb0e3SMichael Baum 		loop = part;
2791256fb0e3SMichael Baum 		/* Store the packet length for legacy MPW. */
2792256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(MPW))
2793256fb0e3SMichael Baum 			eseg->mss = rte_cpu_to_be_16
2794256fb0e3SMichael Baum 					(rte_pktmbuf_data_len(loc->mbuf));
2795256fb0e3SMichael Baum 		for (;;) {
2796256fb0e3SMichael Baum 			uint32_t dlen = rte_pktmbuf_data_len(loc->mbuf);
2797256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
2798256fb0e3SMichael Baum 			/* Update sent data bytes counter. */
2799256fb0e3SMichael Baum 			slen += dlen;
2800256fb0e3SMichael Baum #endif
2801a1e910f5SViacheslav Ovsiienko 			rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
2802256fb0e3SMichael Baum 			mlx5_tx_dseg_ptr
2803256fb0e3SMichael Baum 				(txq, loc, dseg,
2804256fb0e3SMichael Baum 				 rte_pktmbuf_mtod(loc->mbuf, uint8_t *),
2805256fb0e3SMichael Baum 				 dlen, olx);
2806256fb0e3SMichael Baum 			if (unlikely(--loop == 0))
2807256fb0e3SMichael Baum 				break;
2808256fb0e3SMichael Baum 			loc->mbuf = *pkts++;
2809256fb0e3SMichael Baum 			if (likely(loop > 1))
2810256fb0e3SMichael Baum 				rte_prefetch0(*pkts);
2811256fb0e3SMichael Baum 			ret = mlx5_tx_able_to_empw(txq, loc, olx, true);
2812256fb0e3SMichael Baum 			/*
2813256fb0e3SMichael Baum 			 * Unroll the completion code to avoid
2814256fb0e3SMichael Baum 			 * returning variable value - it results in
2815256fb0e3SMichael Baum 			 * unoptimized sequent checking in caller.
2816256fb0e3SMichael Baum 			 */
2817256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_MULTI) {
2818256fb0e3SMichael Baum 				part -= loop;
2819256fb0e3SMichael Baum 				mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2820256fb0e3SMichael Baum 				if (unlikely(!loc->elts_free ||
2821256fb0e3SMichael Baum 					     !loc->wqe_free))
2822256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_EXIT;
2823256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_MULTI;
2824256fb0e3SMichael Baum 			}
2825256fb0e3SMichael Baum 			MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
2826256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_TSO) {
2827256fb0e3SMichael Baum 				part -= loop;
2828256fb0e3SMichael Baum 				mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2829256fb0e3SMichael Baum 				if (unlikely(!loc->elts_free ||
2830256fb0e3SMichael Baum 					     !loc->wqe_free))
2831256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_EXIT;
2832256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_TSO;
2833256fb0e3SMichael Baum 			}
2834256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_SINGLE) {
2835256fb0e3SMichael Baum 				part -= loop;
2836256fb0e3SMichael Baum 				mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2837256fb0e3SMichael Baum 				if (unlikely(!loc->elts_free ||
2838256fb0e3SMichael Baum 					     !loc->wqe_free))
2839256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_EXIT;
2840256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_SINGLE;
2841256fb0e3SMichael Baum 			}
2842256fb0e3SMichael Baum 			if (ret != MLX5_TXCMP_CODE_EMPW) {
2843256fb0e3SMichael Baum 				MLX5_ASSERT(false);
2844256fb0e3SMichael Baum 				part -= loop;
2845256fb0e3SMichael Baum 				mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2846256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_ERROR;
2847256fb0e3SMichael Baum 			}
2848256fb0e3SMichael Baum 			/*
2849256fb0e3SMichael Baum 			 * Check whether packet parameters coincide
2850256fb0e3SMichael Baum 			 * within assumed eMPW batch:
2851256fb0e3SMichael Baum 			 * - check sum settings
2852256fb0e3SMichael Baum 			 * - metadata value
2853256fb0e3SMichael Baum 			 * - software parser settings
2854256fb0e3SMichael Baum 			 * - packets length (legacy MPW only)
2855256fb0e3SMichael Baum 			 * - scheduling is not required
2856256fb0e3SMichael Baum 			 */
2857256fb0e3SMichael Baum 			if (!mlx5_tx_match_empw(txq, eseg, loc, dlen, olx)) {
2858256fb0e3SMichael Baum 				MLX5_ASSERT(loop);
2859256fb0e3SMichael Baum 				part -= loop;
2860256fb0e3SMichael Baum 				mlx5_tx_sdone_empw(txq, loc, part, slen, olx);
2861256fb0e3SMichael Baum 				if (unlikely(!loc->elts_free ||
2862256fb0e3SMichael Baum 					     !loc->wqe_free))
2863256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_EXIT;
2864256fb0e3SMichael Baum 				pkts_n -= part;
2865256fb0e3SMichael Baum 				goto next_empw;
2866256fb0e3SMichael Baum 			}
2867256fb0e3SMichael Baum 			/* Packet attributes match, continue the same eMPW. */
2868256fb0e3SMichael Baum 			++dseg;
2869256fb0e3SMichael Baum 			if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
2870256fb0e3SMichael Baum 				dseg = (struct mlx5_wqe_dseg *)txq->wqes;
2871256fb0e3SMichael Baum 		}
2872256fb0e3SMichael Baum 		/* eMPW is built successfully, update loop parameters. */
2873256fb0e3SMichael Baum 		MLX5_ASSERT(!loop);
2874256fb0e3SMichael Baum 		MLX5_ASSERT(pkts_n >= part);
2875256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
2876256fb0e3SMichael Baum 		/* Update sent data bytes counter. */
2877256fb0e3SMichael Baum 		txq->stats.obytes += slen;
2878256fb0e3SMichael Baum #endif
2879256fb0e3SMichael Baum 		loc->elts_free -= part;
2880256fb0e3SMichael Baum 		loc->pkts_sent += part;
2881256fb0e3SMichael Baum 		txq->wqe_ci += (2 + part + 3) / 4;
2882256fb0e3SMichael Baum 		loc->wqe_free -= (2 + part + 3) / 4;
2883256fb0e3SMichael Baum 		pkts_n -= part;
2884256fb0e3SMichael Baum 		if (unlikely(!pkts_n || !loc->elts_free || !loc->wqe_free))
2885256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
2886256fb0e3SMichael Baum 		loc->mbuf = *pkts++;
2887256fb0e3SMichael Baum 		ret = mlx5_tx_able_to_empw(txq, loc, olx, true);
2888256fb0e3SMichael Baum 		if (unlikely(ret != MLX5_TXCMP_CODE_EMPW))
2889256fb0e3SMichael Baum 			return ret;
2890256fb0e3SMichael Baum 		/* Continue sending eMPW batches. */
2891256fb0e3SMichael Baum 	}
2892256fb0e3SMichael Baum 	MLX5_ASSERT(false);
2893256fb0e3SMichael Baum }
2894256fb0e3SMichael Baum 
2895256fb0e3SMichael Baum /**
2896256fb0e3SMichael Baum  * The routine sends packets with MLX5_OPCODE_EMPW
2897256fb0e3SMichael Baum  * with inlining, optionally supports VLAN insertion.
2898256fb0e3SMichael Baum  */
2899256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
2900256fb0e3SMichael Baum mlx5_tx_burst_empw_inline(struct mlx5_txq_data *__rte_restrict txq,
2901256fb0e3SMichael Baum 			  struct rte_mbuf **__rte_restrict pkts,
2902256fb0e3SMichael Baum 			  unsigned int pkts_n,
2903256fb0e3SMichael Baum 			  struct mlx5_txq_local *__rte_restrict loc,
2904256fb0e3SMichael Baum 			  unsigned int olx)
2905256fb0e3SMichael Baum {
2906256fb0e3SMichael Baum 	/*
2907256fb0e3SMichael Baum 	 * Subroutine is the part of mlx5_tx_burst_single() and sends
2908256fb0e3SMichael Baum 	 * single-segment packet with eMPW opcode with data inlining.
2909256fb0e3SMichael Baum 	 */
2910256fb0e3SMichael Baum 	MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
2911256fb0e3SMichael Baum 	MLX5_ASSERT(MLX5_TXOFF_CONFIG(EMPW));
2912256fb0e3SMichael Baum 	MLX5_ASSERT(loc->elts_free && loc->wqe_free);
2913256fb0e3SMichael Baum 	MLX5_ASSERT(pkts_n > loc->pkts_sent);
2914256fb0e3SMichael Baum 	pkts += loc->pkts_sent + 1;
2915256fb0e3SMichael Baum 	pkts_n -= loc->pkts_sent;
2916256fb0e3SMichael Baum 	for (;;) {
2917256fb0e3SMichael Baum 		struct mlx5_wqe_dseg *__rte_restrict dseg;
2918256fb0e3SMichael Baum 		struct mlx5_wqe *__rte_restrict wqem;
2919256fb0e3SMichael Baum 		enum mlx5_txcmp_code ret;
2920256fb0e3SMichael Baum 		unsigned int room, part, nlim;
2921256fb0e3SMichael Baum 		unsigned int slen = 0;
2922256fb0e3SMichael Baum 
2923256fb0e3SMichael Baum 		MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
2924256fb0e3SMichael Baum 		/*
2925256fb0e3SMichael Baum 		 * Limits the amount of packets in one WQE
2926256fb0e3SMichael Baum 		 * to improve CQE latency generation.
2927256fb0e3SMichael Baum 		 */
2928256fb0e3SMichael Baum 		nlim = RTE_MIN(pkts_n, MLX5_TXOFF_CONFIG(MPW) ?
2929256fb0e3SMichael Baum 				       MLX5_MPW_INLINE_MAX_PACKETS :
2930256fb0e3SMichael Baum 				       MLX5_EMPW_MAX_PACKETS);
293137d6fc30SViacheslav Ovsiienko 		if (MLX5_TXOFF_CONFIG(TXPP)) {
293237d6fc30SViacheslav Ovsiienko 			enum mlx5_txcmp_code wret;
293337d6fc30SViacheslav Ovsiienko 
293437d6fc30SViacheslav Ovsiienko 			/* Generate WAIT for scheduling if requested. */
293537d6fc30SViacheslav Ovsiienko 			wret = mlx5_tx_schedule_send(txq, loc, nlim, olx);
293637d6fc30SViacheslav Ovsiienko 			if (wret == MLX5_TXCMP_CODE_EXIT)
293737d6fc30SViacheslav Ovsiienko 				return MLX5_TXCMP_CODE_EXIT;
293837d6fc30SViacheslav Ovsiienko 			if (wret == MLX5_TXCMP_CODE_ERROR)
293937d6fc30SViacheslav Ovsiienko 				return MLX5_TXCMP_CODE_ERROR;
294037d6fc30SViacheslav Ovsiienko 		}
2941256fb0e3SMichael Baum 		/* Check whether we have minimal amount WQEs */
2942256fb0e3SMichael Baum 		if (unlikely(loc->wqe_free <
2943256fb0e3SMichael Baum 			    ((2 + MLX5_EMPW_MIN_PACKETS + 3) / 4)))
2944256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
2945256fb0e3SMichael Baum 		if (likely(pkts_n > 1))
2946256fb0e3SMichael Baum 			rte_prefetch0(*pkts);
2947256fb0e3SMichael Baum 		wqem = txq->wqes + (txq->wqe_ci & txq->wqe_m);
2948256fb0e3SMichael Baum 		/*
2949256fb0e3SMichael Baum 		 * Build eMPW title WQEBB:
2950256fb0e3SMichael Baum 		 * - Control Segment, eMPW opcode, zero DS
2951256fb0e3SMichael Baum 		 * - Ethernet Segment, no inline
2952256fb0e3SMichael Baum 		 */
2953256fb0e3SMichael Baum 		mlx5_tx_cseg_init(txq, loc, wqem, 0,
2954256fb0e3SMichael Baum 				  MLX5_OPCODE_ENHANCED_MPSW, olx);
2955256fb0e3SMichael Baum 		mlx5_tx_eseg_none(txq, loc, wqem,
2956256fb0e3SMichael Baum 				  olx & ~MLX5_TXOFF_CONFIG_VLAN);
2957256fb0e3SMichael Baum 		dseg = &wqem->dseg[0];
2958256fb0e3SMichael Baum 		/* Store the packet length for legacy MPW. */
2959256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(MPW))
2960256fb0e3SMichael Baum 			wqem->eseg.mss = rte_cpu_to_be_16
2961256fb0e3SMichael Baum 					 (rte_pktmbuf_data_len(loc->mbuf));
2962256fb0e3SMichael Baum 		room = RTE_MIN(MLX5_WQE_SIZE_MAX / MLX5_WQE_SIZE,
2963256fb0e3SMichael Baum 			       loc->wqe_free) * MLX5_WQE_SIZE -
2964256fb0e3SMichael Baum 					MLX5_WQE_CSEG_SIZE -
2965256fb0e3SMichael Baum 					MLX5_WQE_ESEG_SIZE;
2966256fb0e3SMichael Baum 		/* Limit the room for legacy MPW sessions for performance. */
2967256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(MPW))
2968256fb0e3SMichael Baum 			room = RTE_MIN(room,
2969256fb0e3SMichael Baum 				       RTE_MAX(txq->inlen_empw +
2970256fb0e3SMichael Baum 					       sizeof(dseg->bcount) +
2971256fb0e3SMichael Baum 					       (MLX5_TXOFF_CONFIG(VLAN) ?
2972256fb0e3SMichael Baum 					       sizeof(struct rte_vlan_hdr) : 0),
2973256fb0e3SMichael Baum 					       MLX5_MPW_INLINE_MAX_PACKETS *
2974256fb0e3SMichael Baum 					       MLX5_WQE_DSEG_SIZE));
2975256fb0e3SMichael Baum 		/* Build WQE till we have space, packets and resources. */
2976256fb0e3SMichael Baum 		part = room;
2977256fb0e3SMichael Baum 		for (;;) {
2978256fb0e3SMichael Baum 			uint32_t dlen = rte_pktmbuf_data_len(loc->mbuf);
2979256fb0e3SMichael Baum 			uint8_t *dptr = rte_pktmbuf_mtod(loc->mbuf, uint8_t *);
2980256fb0e3SMichael Baum 			unsigned int tlen;
2981256fb0e3SMichael Baum 
2982256fb0e3SMichael Baum 			MLX5_ASSERT(room >= MLX5_WQE_DSEG_SIZE);
2983256fb0e3SMichael Baum 			MLX5_ASSERT((room % MLX5_WQE_DSEG_SIZE) == 0);
2984256fb0e3SMichael Baum 			MLX5_ASSERT((uintptr_t)dseg < (uintptr_t)txq->wqes_end);
2985256fb0e3SMichael Baum 			/*
2986256fb0e3SMichael Baum 			 * Some Tx offloads may cause an error if packet is not
2987256fb0e3SMichael Baum 			 * long enough, check against assumed minimal length.
2988256fb0e3SMichael Baum 			 */
2989256fb0e3SMichael Baum 			if (unlikely(dlen <= MLX5_ESEG_MIN_INLINE_SIZE)) {
2990256fb0e3SMichael Baum 				part -= room;
2991256fb0e3SMichael Baum 				if (unlikely(!part))
2992256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_ERROR;
2993256fb0e3SMichael Baum 				/*
2994256fb0e3SMichael Baum 				 * We have some successfully built
2995256fb0e3SMichael Baum 				 * packet Data Segments to send.
2996256fb0e3SMichael Baum 				 */
2997256fb0e3SMichael Baum 				mlx5_tx_idone_empw(txq, loc, part,
2998256fb0e3SMichael Baum 						   slen, wqem, olx);
2999256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_ERROR;
3000256fb0e3SMichael Baum 			}
3001256fb0e3SMichael Baum 			/* Inline or not inline - that's the Question. */
3002256fb0e3SMichael Baum 			if (dlen > txq->inlen_empw ||
3003daa02b5cSOlivier Matz 			    loc->mbuf->ol_flags & RTE_MBUF_F_TX_DYNF_NOINLINE)
3004256fb0e3SMichael Baum 				goto pointer_empw;
3005256fb0e3SMichael Baum 			if (MLX5_TXOFF_CONFIG(MPW)) {
3006256fb0e3SMichael Baum 				if (dlen > txq->inlen_send)
3007256fb0e3SMichael Baum 					goto pointer_empw;
3008256fb0e3SMichael Baum 				tlen = dlen;
3009256fb0e3SMichael Baum 				if (part == room) {
3010256fb0e3SMichael Baum 					/* Open new inline MPW session. */
3011256fb0e3SMichael Baum 					tlen += sizeof(dseg->bcount);
3012256fb0e3SMichael Baum 					dseg->bcount = RTE_BE32(0);
3013256fb0e3SMichael Baum 					dseg = RTE_PTR_ADD
3014256fb0e3SMichael Baum 						(dseg, sizeof(dseg->bcount));
3015256fb0e3SMichael Baum 				} else {
3016256fb0e3SMichael Baum 					/*
3017256fb0e3SMichael Baum 					 * No pointer and inline descriptor
3018256fb0e3SMichael Baum 					 * intermix for legacy MPW sessions.
3019256fb0e3SMichael Baum 					 */
3020256fb0e3SMichael Baum 					if (wqem->dseg[0].bcount)
3021256fb0e3SMichael Baum 						break;
3022256fb0e3SMichael Baum 				}
3023256fb0e3SMichael Baum 			} else {
3024256fb0e3SMichael Baum 				tlen = sizeof(dseg->bcount) + dlen;
3025256fb0e3SMichael Baum 			}
3026256fb0e3SMichael Baum 			/* Inline entire packet, optional VLAN insertion. */
3027256fb0e3SMichael Baum 			if (MLX5_TXOFF_CONFIG(VLAN) &&
3028daa02b5cSOlivier Matz 			    loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) {
3029256fb0e3SMichael Baum 				/*
3030256fb0e3SMichael Baum 				 * The packet length must be checked in
3031256fb0e3SMichael Baum 				 * mlx5_tx_able_to_empw() and packet
3032256fb0e3SMichael Baum 				 * fits into inline length guaranteed.
3033256fb0e3SMichael Baum 				 */
3034256fb0e3SMichael Baum 				MLX5_ASSERT((dlen +
3035256fb0e3SMichael Baum 					     sizeof(struct rte_vlan_hdr)) <=
3036256fb0e3SMichael Baum 					    txq->inlen_empw);
3037256fb0e3SMichael Baum 				tlen += sizeof(struct rte_vlan_hdr);
3038256fb0e3SMichael Baum 				if (room < tlen)
3039256fb0e3SMichael Baum 					break;
3040a1e910f5SViacheslav Ovsiienko 				rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3041256fb0e3SMichael Baum 				dseg = mlx5_tx_dseg_vlan(txq, loc, dseg,
3042256fb0e3SMichael Baum 							 dptr, dlen, olx);
3043256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
3044256fb0e3SMichael Baum 				/* Update sent data bytes counter. */
3045256fb0e3SMichael Baum 				slen +=	sizeof(struct rte_vlan_hdr);
3046256fb0e3SMichael Baum #endif
3047256fb0e3SMichael Baum 			} else {
3048256fb0e3SMichael Baum 				if (room < tlen)
3049256fb0e3SMichael Baum 					break;
3050a1e910f5SViacheslav Ovsiienko 				rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3051256fb0e3SMichael Baum 				dseg = mlx5_tx_dseg_empw(txq, loc, dseg,
3052256fb0e3SMichael Baum 							 dptr, dlen, olx);
3053256fb0e3SMichael Baum 			}
3054256fb0e3SMichael Baum 			if (!MLX5_TXOFF_CONFIG(MPW))
3055256fb0e3SMichael Baum 				tlen = RTE_ALIGN(tlen, MLX5_WSEG_SIZE);
3056256fb0e3SMichael Baum 			MLX5_ASSERT(room >= tlen);
3057256fb0e3SMichael Baum 			room -= tlen;
3058256fb0e3SMichael Baum 			/*
3059256fb0e3SMichael Baum 			 * Packet data are completely inline,
3060256fb0e3SMichael Baum 			 * we can try to free the packet.
3061256fb0e3SMichael Baum 			 */
3062256fb0e3SMichael Baum 			if (likely(loc->pkts_sent == loc->mbuf_free)) {
3063256fb0e3SMichael Baum 				/*
3064256fb0e3SMichael Baum 				 * All the packets from the burst beginning
3065256fb0e3SMichael Baum 				 * are inline, we can free mbufs directly
3066256fb0e3SMichael Baum 				 * from the origin array on tx_burst exit().
3067256fb0e3SMichael Baum 				 */
3068256fb0e3SMichael Baum 				loc->mbuf_free++;
3069256fb0e3SMichael Baum 				goto next_mbuf;
3070256fb0e3SMichael Baum 			}
3071256fb0e3SMichael Baum 			/*
3072256fb0e3SMichael Baum 			 * In order no to call rte_pktmbuf_free_seg() here,
3073256fb0e3SMichael Baum 			 * in the most inner loop (that might be very
3074256fb0e3SMichael Baum 			 * expensive) we just save the mbuf in elts.
3075256fb0e3SMichael Baum 			 */
3076256fb0e3SMichael Baum 			txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
3077256fb0e3SMichael Baum 			loc->elts_free--;
3078256fb0e3SMichael Baum 			goto next_mbuf;
3079256fb0e3SMichael Baum pointer_empw:
3080256fb0e3SMichael Baum 			/*
3081256fb0e3SMichael Baum 			 * No pointer and inline descriptor
3082256fb0e3SMichael Baum 			 * intermix for legacy MPW sessions.
3083256fb0e3SMichael Baum 			 */
3084256fb0e3SMichael Baum 			if (MLX5_TXOFF_CONFIG(MPW) &&
3085256fb0e3SMichael Baum 			    part != room &&
3086256fb0e3SMichael Baum 			    wqem->dseg[0].bcount == RTE_BE32(0))
3087256fb0e3SMichael Baum 				break;
3088256fb0e3SMichael Baum 			/*
3089256fb0e3SMichael Baum 			 * Not inlinable VLAN packets are
3090256fb0e3SMichael Baum 			 * proceeded outside of this routine.
3091256fb0e3SMichael Baum 			 */
3092256fb0e3SMichael Baum 			MLX5_ASSERT(room >= MLX5_WQE_DSEG_SIZE);
3093256fb0e3SMichael Baum 			if (MLX5_TXOFF_CONFIG(VLAN))
3094256fb0e3SMichael Baum 				MLX5_ASSERT(!(loc->mbuf->ol_flags &
3095daa02b5cSOlivier Matz 					    RTE_MBUF_F_TX_VLAN));
3096a1e910f5SViacheslav Ovsiienko 			rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3097256fb0e3SMichael Baum 			mlx5_tx_dseg_ptr(txq, loc, dseg, dptr, dlen, olx);
3098256fb0e3SMichael Baum 			/* We have to store mbuf in elts.*/
3099256fb0e3SMichael Baum 			txq->elts[txq->elts_head++ & txq->elts_m] = loc->mbuf;
3100256fb0e3SMichael Baum 			loc->elts_free--;
3101256fb0e3SMichael Baum 			room -= MLX5_WQE_DSEG_SIZE;
3102256fb0e3SMichael Baum 			/* Ring buffer wraparound is checked at the loop end.*/
3103256fb0e3SMichael Baum 			++dseg;
3104256fb0e3SMichael Baum next_mbuf:
3105256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
3106256fb0e3SMichael Baum 			/* Update sent data bytes counter. */
3107256fb0e3SMichael Baum 			slen += dlen;
3108256fb0e3SMichael Baum #endif
3109256fb0e3SMichael Baum 			loc->pkts_sent++;
3110256fb0e3SMichael Baum 			pkts_n--;
3111256fb0e3SMichael Baum 			if (unlikely(!pkts_n || !loc->elts_free)) {
3112256fb0e3SMichael Baum 				/*
3113256fb0e3SMichael Baum 				 * We have no resources/packets to
3114256fb0e3SMichael Baum 				 * continue build descriptors.
3115256fb0e3SMichael Baum 				 */
3116256fb0e3SMichael Baum 				part -= room;
3117256fb0e3SMichael Baum 				mlx5_tx_idone_empw(txq, loc, part,
3118256fb0e3SMichael Baum 						   slen, wqem, olx);
3119256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_EXIT;
3120256fb0e3SMichael Baum 			}
3121256fb0e3SMichael Baum 			loc->mbuf = *pkts++;
3122256fb0e3SMichael Baum 			if (likely(pkts_n > 1))
3123256fb0e3SMichael Baum 				rte_prefetch0(*pkts);
3124256fb0e3SMichael Baum 			ret = mlx5_tx_able_to_empw(txq, loc, olx, true);
3125256fb0e3SMichael Baum 			/*
3126256fb0e3SMichael Baum 			 * Unroll the completion code to avoid
3127256fb0e3SMichael Baum 			 * returning variable value - it results in
3128256fb0e3SMichael Baum 			 * unoptimized sequent checking in caller.
3129256fb0e3SMichael Baum 			 */
3130256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_MULTI) {
3131256fb0e3SMichael Baum 				part -= room;
3132256fb0e3SMichael Baum 				mlx5_tx_idone_empw(txq, loc, part,
3133256fb0e3SMichael Baum 						   slen, wqem, olx);
3134256fb0e3SMichael Baum 				if (unlikely(!loc->elts_free ||
3135256fb0e3SMichael Baum 					     !loc->wqe_free))
3136256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_EXIT;
3137256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_MULTI;
3138256fb0e3SMichael Baum 			}
3139256fb0e3SMichael Baum 			MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
3140256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_TSO) {
3141256fb0e3SMichael Baum 				part -= room;
3142256fb0e3SMichael Baum 				mlx5_tx_idone_empw(txq, loc, part,
3143256fb0e3SMichael Baum 						   slen, wqem, olx);
3144256fb0e3SMichael Baum 				if (unlikely(!loc->elts_free ||
3145256fb0e3SMichael Baum 					     !loc->wqe_free))
3146256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_EXIT;
3147256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_TSO;
3148256fb0e3SMichael Baum 			}
3149256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_SINGLE) {
3150256fb0e3SMichael Baum 				part -= room;
3151256fb0e3SMichael Baum 				mlx5_tx_idone_empw(txq, loc, part,
3152256fb0e3SMichael Baum 						   slen, wqem, olx);
3153256fb0e3SMichael Baum 				if (unlikely(!loc->elts_free ||
3154256fb0e3SMichael Baum 					     !loc->wqe_free))
3155256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_EXIT;
3156256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_SINGLE;
3157256fb0e3SMichael Baum 			}
3158256fb0e3SMichael Baum 			if (ret != MLX5_TXCMP_CODE_EMPW) {
3159256fb0e3SMichael Baum 				MLX5_ASSERT(false);
3160256fb0e3SMichael Baum 				part -= room;
3161256fb0e3SMichael Baum 				mlx5_tx_idone_empw(txq, loc, part,
3162256fb0e3SMichael Baum 						   slen, wqem, olx);
3163256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_ERROR;
3164256fb0e3SMichael Baum 			}
3165256fb0e3SMichael Baum 			/* Check if we have minimal room left. */
3166256fb0e3SMichael Baum 			nlim--;
3167256fb0e3SMichael Baum 			if (unlikely(!nlim || room < MLX5_WQE_DSEG_SIZE))
3168256fb0e3SMichael Baum 				break;
3169256fb0e3SMichael Baum 			/*
3170256fb0e3SMichael Baum 			 * Check whether packet parameters coincide
3171256fb0e3SMichael Baum 			 * within assumed eMPW batch:
3172256fb0e3SMichael Baum 			 * - check sum settings
3173256fb0e3SMichael Baum 			 * - metadata value
3174256fb0e3SMichael Baum 			 * - software parser settings
3175256fb0e3SMichael Baum 			 * - packets length (legacy MPW only)
3176256fb0e3SMichael Baum 			 * - scheduling is not required
3177256fb0e3SMichael Baum 			 */
3178256fb0e3SMichael Baum 			if (!mlx5_tx_match_empw(txq, &wqem->eseg,
3179256fb0e3SMichael Baum 						loc, dlen, olx))
3180256fb0e3SMichael Baum 				break;
3181256fb0e3SMichael Baum 			/* Packet attributes match, continue the same eMPW. */
3182256fb0e3SMichael Baum 			if ((uintptr_t)dseg >= (uintptr_t)txq->wqes_end)
3183256fb0e3SMichael Baum 				dseg = (struct mlx5_wqe_dseg *)txq->wqes;
3184256fb0e3SMichael Baum 		}
3185256fb0e3SMichael Baum 		/*
3186256fb0e3SMichael Baum 		 * We get here to close an existing eMPW
3187256fb0e3SMichael Baum 		 * session and start the new one.
3188256fb0e3SMichael Baum 		 */
3189256fb0e3SMichael Baum 		MLX5_ASSERT(pkts_n);
3190256fb0e3SMichael Baum 		part -= room;
3191256fb0e3SMichael Baum 		if (unlikely(!part))
3192256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
3193256fb0e3SMichael Baum 		mlx5_tx_idone_empw(txq, loc, part, slen, wqem, olx);
3194256fb0e3SMichael Baum 		if (unlikely(!loc->elts_free ||
3195256fb0e3SMichael Baum 			     !loc->wqe_free))
3196256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
3197256fb0e3SMichael Baum 		/* Continue the loop with new eMPW session. */
3198256fb0e3SMichael Baum 	}
3199256fb0e3SMichael Baum 	MLX5_ASSERT(false);
3200256fb0e3SMichael Baum }
3201256fb0e3SMichael Baum 
3202256fb0e3SMichael Baum /**
3203256fb0e3SMichael Baum  * The routine sends packets with ordinary MLX5_OPCODE_SEND.
3204256fb0e3SMichael Baum  * Data inlining and VLAN insertion are supported.
3205256fb0e3SMichael Baum  */
3206256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
3207256fb0e3SMichael Baum mlx5_tx_burst_single_send(struct mlx5_txq_data *__rte_restrict txq,
3208256fb0e3SMichael Baum 			  struct rte_mbuf **__rte_restrict pkts,
3209256fb0e3SMichael Baum 			  unsigned int pkts_n,
3210256fb0e3SMichael Baum 			  struct mlx5_txq_local *__rte_restrict loc,
3211256fb0e3SMichael Baum 			  unsigned int olx)
3212256fb0e3SMichael Baum {
3213256fb0e3SMichael Baum 	/*
3214256fb0e3SMichael Baum 	 * Subroutine is the part of mlx5_tx_burst_single()
3215256fb0e3SMichael Baum 	 * and sends single-segment packet with SEND opcode.
3216256fb0e3SMichael Baum 	 */
3217256fb0e3SMichael Baum 	MLX5_ASSERT(loc->elts_free && loc->wqe_free);
3218256fb0e3SMichael Baum 	MLX5_ASSERT(pkts_n > loc->pkts_sent);
3219256fb0e3SMichael Baum 	pkts += loc->pkts_sent + 1;
3220256fb0e3SMichael Baum 	pkts_n -= loc->pkts_sent;
3221256fb0e3SMichael Baum 	for (;;) {
3222256fb0e3SMichael Baum 		struct mlx5_wqe *__rte_restrict wqe;
3223256fb0e3SMichael Baum 		enum mlx5_txcmp_code ret;
3224256fb0e3SMichael Baum 
3225256fb0e3SMichael Baum 		MLX5_ASSERT(NB_SEGS(loc->mbuf) == 1);
322637d6fc30SViacheslav Ovsiienko 		MLX5_ASSERT(loc->elts_free);
3227256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(TXPP)) {
3228256fb0e3SMichael Baum 			enum mlx5_txcmp_code wret;
3229256fb0e3SMichael Baum 
3230256fb0e3SMichael Baum 			/* Generate WAIT for scheduling if requested. */
323137d6fc30SViacheslav Ovsiienko 			wret = mlx5_tx_schedule_send(txq, loc, 0, olx);
3232256fb0e3SMichael Baum 			if (wret == MLX5_TXCMP_CODE_EXIT)
3233256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_EXIT;
3234256fb0e3SMichael Baum 			if (wret == MLX5_TXCMP_CODE_ERROR)
3235256fb0e3SMichael Baum 				return MLX5_TXCMP_CODE_ERROR;
3236256fb0e3SMichael Baum 		}
3237256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(INLINE)) {
3238256fb0e3SMichael Baum 			unsigned int inlen, vlan = 0;
3239256fb0e3SMichael Baum 
3240256fb0e3SMichael Baum 			inlen = rte_pktmbuf_data_len(loc->mbuf);
3241256fb0e3SMichael Baum 			if (MLX5_TXOFF_CONFIG(VLAN) &&
3242daa02b5cSOlivier Matz 			    loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN) {
3243256fb0e3SMichael Baum 				vlan = sizeof(struct rte_vlan_hdr);
3244256fb0e3SMichael Baum 				inlen += vlan;
3245256fb0e3SMichael Baum 			}
3246256fb0e3SMichael Baum 			/*
3247256fb0e3SMichael Baum 			 * If inlining is enabled at configuration time
3248256fb0e3SMichael Baum 			 * the limit must be not less than minimal size.
3249256fb0e3SMichael Baum 			 * Otherwise we would do extra check for data
3250256fb0e3SMichael Baum 			 * size to avoid crashes due to length overflow.
3251256fb0e3SMichael Baum 			 */
3252256fb0e3SMichael Baum 			MLX5_ASSERT(txq->inlen_send >=
3253256fb0e3SMichael Baum 				    MLX5_ESEG_MIN_INLINE_SIZE);
3254256fb0e3SMichael Baum 			if (inlen <= txq->inlen_send) {
3255256fb0e3SMichael Baum 				unsigned int seg_n, wqe_n;
3256256fb0e3SMichael Baum 
3257256fb0e3SMichael Baum 				rte_prefetch0(rte_pktmbuf_mtod
3258256fb0e3SMichael Baum 						(loc->mbuf, uint8_t *));
3259256fb0e3SMichael Baum 				/* Check against minimal length. */
3260256fb0e3SMichael Baum 				if (inlen <= MLX5_ESEG_MIN_INLINE_SIZE)
3261256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_ERROR;
3262256fb0e3SMichael Baum 				if (loc->mbuf->ol_flags &
3263daa02b5cSOlivier Matz 				    RTE_MBUF_F_TX_DYNF_NOINLINE) {
3264256fb0e3SMichael Baum 					/*
3265256fb0e3SMichael Baum 					 * The hint flag not to inline packet
3266256fb0e3SMichael Baum 					 * data is set. Check whether we can
3267256fb0e3SMichael Baum 					 * follow the hint.
3268256fb0e3SMichael Baum 					 */
3269256fb0e3SMichael Baum 					if ((!MLX5_TXOFF_CONFIG(EMPW) &&
3270256fb0e3SMichael Baum 					      txq->inlen_mode) ||
3271256fb0e3SMichael Baum 					    (MLX5_TXOFF_CONFIG(MPW) &&
3272256fb0e3SMichael Baum 					     txq->inlen_mode)) {
3273256fb0e3SMichael Baum 						if (inlen <= txq->inlen_send)
3274256fb0e3SMichael Baum 							goto single_inline;
3275256fb0e3SMichael Baum 						/*
3276256fb0e3SMichael Baum 						 * The hardware requires the
3277256fb0e3SMichael Baum 						 * minimal inline data header.
3278256fb0e3SMichael Baum 						 */
3279256fb0e3SMichael Baum 						goto single_min_inline;
3280256fb0e3SMichael Baum 					}
3281256fb0e3SMichael Baum 					if (MLX5_TXOFF_CONFIG(VLAN) &&
3282256fb0e3SMichael Baum 					    vlan && !txq->vlan_en) {
3283256fb0e3SMichael Baum 						/*
3284256fb0e3SMichael Baum 						 * We must insert VLAN tag
3285256fb0e3SMichael Baum 						 * by software means.
3286256fb0e3SMichael Baum 						 */
3287256fb0e3SMichael Baum 						goto single_part_inline;
3288256fb0e3SMichael Baum 					}
3289256fb0e3SMichael Baum 					goto single_no_inline;
3290256fb0e3SMichael Baum 				}
3291256fb0e3SMichael Baum single_inline:
3292256fb0e3SMichael Baum 				/*
3293256fb0e3SMichael Baum 				 * Completely inlined packet data WQE:
3294256fb0e3SMichael Baum 				 * - Control Segment, SEND opcode
3295256fb0e3SMichael Baum 				 * - Ethernet Segment, no VLAN insertion
3296256fb0e3SMichael Baum 				 * - Data inlined, VLAN optionally inserted
3297256fb0e3SMichael Baum 				 * - Alignment to MLX5_WSEG_SIZE
3298256fb0e3SMichael Baum 				 * Have to estimate amount of WQEBBs
3299256fb0e3SMichael Baum 				 */
3300256fb0e3SMichael Baum 				seg_n = (inlen + 3 * MLX5_WSEG_SIZE -
3301256fb0e3SMichael Baum 					 MLX5_ESEG_MIN_INLINE_SIZE +
3302256fb0e3SMichael Baum 					 MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
3303256fb0e3SMichael Baum 				/* Check if there are enough WQEBBs. */
3304256fb0e3SMichael Baum 				wqe_n = (seg_n + 3) / 4;
3305256fb0e3SMichael Baum 				if (wqe_n > loc->wqe_free)
3306256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_EXIT;
3307256fb0e3SMichael Baum 				wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
3308256fb0e3SMichael Baum 				loc->wqe_last = wqe;
3309256fb0e3SMichael Baum 				mlx5_tx_cseg_init(txq, loc, wqe, seg_n,
3310256fb0e3SMichael Baum 						  MLX5_OPCODE_SEND, olx);
3311a1e910f5SViacheslav Ovsiienko 				rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3312256fb0e3SMichael Baum 				mlx5_tx_eseg_data(txq, loc, wqe,
3313256fb0e3SMichael Baum 						  vlan, inlen, 0, olx);
3314256fb0e3SMichael Baum 				txq->wqe_ci += wqe_n;
3315256fb0e3SMichael Baum 				loc->wqe_free -= wqe_n;
3316256fb0e3SMichael Baum 				/*
3317256fb0e3SMichael Baum 				 * Packet data are completely inlined,
3318256fb0e3SMichael Baum 				 * free the packet immediately.
3319256fb0e3SMichael Baum 				 */
3320256fb0e3SMichael Baum 				rte_pktmbuf_free_seg(loc->mbuf);
3321256fb0e3SMichael Baum 			} else if ((!MLX5_TXOFF_CONFIG(EMPW) ||
3322256fb0e3SMichael Baum 				     MLX5_TXOFF_CONFIG(MPW)) &&
3323256fb0e3SMichael Baum 					txq->inlen_mode) {
3324256fb0e3SMichael Baum 				/*
3325256fb0e3SMichael Baum 				 * If minimal inlining is requested the eMPW
3326256fb0e3SMichael Baum 				 * feature should be disabled due to data is
3327256fb0e3SMichael Baum 				 * inlined into Ethernet Segment, which can
3328256fb0e3SMichael Baum 				 * not contain inlined data for eMPW due to
3329256fb0e3SMichael Baum 				 * segment shared for all packets.
3330256fb0e3SMichael Baum 				 */
3331256fb0e3SMichael Baum 				struct mlx5_wqe_dseg *__rte_restrict dseg;
3332256fb0e3SMichael Baum 				unsigned int ds;
3333256fb0e3SMichael Baum 				uint8_t *dptr;
3334256fb0e3SMichael Baum 
3335256fb0e3SMichael Baum 				/*
3336256fb0e3SMichael Baum 				 * The inline-mode settings require
3337256fb0e3SMichael Baum 				 * to inline the specified amount of
3338256fb0e3SMichael Baum 				 * data bytes to the Ethernet Segment.
3339256fb0e3SMichael Baum 				 * We should check the free space in
3340256fb0e3SMichael Baum 				 * WQE ring buffer to inline partially.
3341256fb0e3SMichael Baum 				 */
3342256fb0e3SMichael Baum single_min_inline:
3343256fb0e3SMichael Baum 				MLX5_ASSERT(txq->inlen_send >= txq->inlen_mode);
3344256fb0e3SMichael Baum 				MLX5_ASSERT(inlen > txq->inlen_mode);
3345256fb0e3SMichael Baum 				MLX5_ASSERT(txq->inlen_mode >=
3346256fb0e3SMichael Baum 					    MLX5_ESEG_MIN_INLINE_SIZE);
3347256fb0e3SMichael Baum 				/*
3348256fb0e3SMichael Baum 				 * Check whether there are enough free WQEBBs:
3349256fb0e3SMichael Baum 				 * - Control Segment
3350256fb0e3SMichael Baum 				 * - Ethernet Segment
3351256fb0e3SMichael Baum 				 * - First Segment of inlined Ethernet data
3352256fb0e3SMichael Baum 				 * - ... data continued ...
3353256fb0e3SMichael Baum 				 * - Finishing Data Segment of pointer type
3354256fb0e3SMichael Baum 				 */
3355256fb0e3SMichael Baum 				ds = (MLX5_WQE_CSEG_SIZE +
3356256fb0e3SMichael Baum 				      MLX5_WQE_ESEG_SIZE +
3357256fb0e3SMichael Baum 				      MLX5_WQE_DSEG_SIZE +
3358256fb0e3SMichael Baum 				      txq->inlen_mode -
3359256fb0e3SMichael Baum 				      MLX5_ESEG_MIN_INLINE_SIZE +
3360256fb0e3SMichael Baum 				      MLX5_WQE_DSEG_SIZE +
3361256fb0e3SMichael Baum 				      MLX5_WSEG_SIZE - 1) / MLX5_WSEG_SIZE;
3362256fb0e3SMichael Baum 				if (loc->wqe_free < ((ds + 3) / 4))
3363256fb0e3SMichael Baum 					return MLX5_TXCMP_CODE_EXIT;
3364256fb0e3SMichael Baum 				/*
3365256fb0e3SMichael Baum 				 * Build the ordinary SEND WQE:
3366256fb0e3SMichael Baum 				 * - Control Segment
3367256fb0e3SMichael Baum 				 * - Ethernet Segment, inline inlen_mode bytes
3368256fb0e3SMichael Baum 				 * - Data Segment of pointer type
3369256fb0e3SMichael Baum 				 */
3370256fb0e3SMichael Baum 				wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
3371256fb0e3SMichael Baum 				loc->wqe_last = wqe;
3372256fb0e3SMichael Baum 				mlx5_tx_cseg_init(txq, loc, wqe, ds,
3373256fb0e3SMichael Baum 						  MLX5_OPCODE_SEND, olx);
3374a1e910f5SViacheslav Ovsiienko 				rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3375256fb0e3SMichael Baum 				dseg = mlx5_tx_eseg_data(txq, loc, wqe, vlan,
3376256fb0e3SMichael Baum 							 txq->inlen_mode,
3377256fb0e3SMichael Baum 							 0, olx);
3378256fb0e3SMichael Baum 				dptr = rte_pktmbuf_mtod(loc->mbuf, uint8_t *) +
3379256fb0e3SMichael Baum 				       txq->inlen_mode - vlan;
3380256fb0e3SMichael Baum 				inlen -= txq->inlen_mode;
3381256fb0e3SMichael Baum 				mlx5_tx_dseg_ptr(txq, loc, dseg,
3382256fb0e3SMichael Baum 						 dptr, inlen, olx);
3383256fb0e3SMichael Baum 				/*
3384256fb0e3SMichael Baum 				 * WQE is built, update the loop parameters
3385256fb0e3SMichael Baum 				 * and got to the next packet.
3386256fb0e3SMichael Baum 				 */
3387256fb0e3SMichael Baum 				txq->wqe_ci += (ds + 3) / 4;
3388256fb0e3SMichael Baum 				loc->wqe_free -= (ds + 3) / 4;
3389256fb0e3SMichael Baum 				/* We have to store mbuf in elts.*/
3390256fb0e3SMichael Baum 				MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
3391256fb0e3SMichael Baum 				txq->elts[txq->elts_head++ & txq->elts_m] =
3392256fb0e3SMichael Baum 						loc->mbuf;
3393256fb0e3SMichael Baum 				--loc->elts_free;
3394256fb0e3SMichael Baum 			} else {
3395256fb0e3SMichael Baum 				uint8_t *dptr;
3396256fb0e3SMichael Baum 				unsigned int dlen;
3397256fb0e3SMichael Baum 
3398256fb0e3SMichael Baum 				/*
3399256fb0e3SMichael Baum 				 * Partially inlined packet data WQE, we have
3400256fb0e3SMichael Baum 				 * some space in title WQEBB, we can fill it
3401256fb0e3SMichael Baum 				 * with some packet data. It takes one WQEBB,
3402256fb0e3SMichael Baum 				 * it is available, no extra space check:
3403256fb0e3SMichael Baum 				 * - Control Segment, SEND opcode
3404256fb0e3SMichael Baum 				 * - Ethernet Segment, no VLAN insertion
3405256fb0e3SMichael Baum 				 * - MLX5_ESEG_MIN_INLINE_SIZE bytes of Data
3406256fb0e3SMichael Baum 				 * - Data Segment, pointer type
3407256fb0e3SMichael Baum 				 *
3408256fb0e3SMichael Baum 				 * We also get here if VLAN insertion is not
3409256fb0e3SMichael Baum 				 * supported by HW, the inline is enabled.
3410256fb0e3SMichael Baum 				 */
3411256fb0e3SMichael Baum single_part_inline:
3412256fb0e3SMichael Baum 				wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
3413256fb0e3SMichael Baum 				loc->wqe_last = wqe;
3414256fb0e3SMichael Baum 				mlx5_tx_cseg_init(txq, loc, wqe, 4,
3415256fb0e3SMichael Baum 						  MLX5_OPCODE_SEND, olx);
3416a1e910f5SViacheslav Ovsiienko 				rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3417256fb0e3SMichael Baum 				mlx5_tx_eseg_dmin(txq, loc, wqe, vlan, olx);
3418256fb0e3SMichael Baum 				dptr = rte_pktmbuf_mtod(loc->mbuf, uint8_t *) +
3419256fb0e3SMichael Baum 				       MLX5_ESEG_MIN_INLINE_SIZE - vlan;
3420256fb0e3SMichael Baum 				/*
3421256fb0e3SMichael Baum 				 * The length check is performed above, by
3422256fb0e3SMichael Baum 				 * comparing with txq->inlen_send. We should
3423256fb0e3SMichael Baum 				 * not get overflow here.
3424256fb0e3SMichael Baum 				 */
3425256fb0e3SMichael Baum 				MLX5_ASSERT(inlen > MLX5_ESEG_MIN_INLINE_SIZE);
3426256fb0e3SMichael Baum 				dlen = inlen - MLX5_ESEG_MIN_INLINE_SIZE;
3427256fb0e3SMichael Baum 				mlx5_tx_dseg_ptr(txq, loc, &wqe->dseg[1],
3428256fb0e3SMichael Baum 						 dptr, dlen, olx);
3429256fb0e3SMichael Baum 				++txq->wqe_ci;
3430256fb0e3SMichael Baum 				--loc->wqe_free;
3431256fb0e3SMichael Baum 				/* We have to store mbuf in elts.*/
3432256fb0e3SMichael Baum 				MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE));
3433256fb0e3SMichael Baum 				txq->elts[txq->elts_head++ & txq->elts_m] =
3434256fb0e3SMichael Baum 						loc->mbuf;
3435256fb0e3SMichael Baum 				--loc->elts_free;
3436256fb0e3SMichael Baum 			}
3437256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
3438256fb0e3SMichael Baum 			/* Update sent data bytes counter. */
3439256fb0e3SMichael Baum 			txq->stats.obytes += vlan +
3440256fb0e3SMichael Baum 					rte_pktmbuf_data_len(loc->mbuf);
3441256fb0e3SMichael Baum #endif
3442256fb0e3SMichael Baum 		} else {
3443256fb0e3SMichael Baum 			/*
3444256fb0e3SMichael Baum 			 * No inline at all, it means the CPU cycles saving
3445256fb0e3SMichael Baum 			 * is prioritized at configuration, we should not
3446256fb0e3SMichael Baum 			 * copy any packet data to WQE.
3447256fb0e3SMichael Baum 			 *
3448256fb0e3SMichael Baum 			 * SEND WQE, one WQEBB:
3449256fb0e3SMichael Baum 			 * - Control Segment, SEND opcode
3450256fb0e3SMichael Baum 			 * - Ethernet Segment, optional VLAN, no inline
3451256fb0e3SMichael Baum 			 * - Data Segment, pointer type
3452256fb0e3SMichael Baum 			 */
3453256fb0e3SMichael Baum single_no_inline:
3454256fb0e3SMichael Baum 			wqe = txq->wqes + (txq->wqe_ci & txq->wqe_m);
3455256fb0e3SMichael Baum 			loc->wqe_last = wqe;
3456256fb0e3SMichael Baum 			mlx5_tx_cseg_init(txq, loc, wqe, 3,
3457256fb0e3SMichael Baum 					  MLX5_OPCODE_SEND, olx);
3458a1e910f5SViacheslav Ovsiienko 			rte_pmd_mlx5_trace_tx_push(loc->mbuf, txq->wqe_ci);
3459256fb0e3SMichael Baum 			mlx5_tx_eseg_none(txq, loc, wqe, olx);
3460256fb0e3SMichael Baum 			mlx5_tx_dseg_ptr
3461256fb0e3SMichael Baum 				(txq, loc, &wqe->dseg[0],
3462256fb0e3SMichael Baum 				 rte_pktmbuf_mtod(loc->mbuf, uint8_t *),
3463256fb0e3SMichael Baum 				 rte_pktmbuf_data_len(loc->mbuf), olx);
3464256fb0e3SMichael Baum 			++txq->wqe_ci;
3465256fb0e3SMichael Baum 			--loc->wqe_free;
3466256fb0e3SMichael Baum 			/*
3467256fb0e3SMichael Baum 			 * We should not store mbuf pointer in elts
3468256fb0e3SMichael Baum 			 * if no inlining is configured, this is done
3469256fb0e3SMichael Baum 			 * by calling routine in a batch copy.
3470256fb0e3SMichael Baum 			 */
3471166f185fSViacheslav Ovsiienko 			if (MLX5_TXOFF_CONFIG(INLINE))
3472166f185fSViacheslav Ovsiienko 				txq->elts[txq->elts_head++ & txq->elts_m] =
3473166f185fSViacheslav Ovsiienko 							loc->mbuf;
3474256fb0e3SMichael Baum 			--loc->elts_free;
3475256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
3476256fb0e3SMichael Baum 			/* Update sent data bytes counter. */
3477256fb0e3SMichael Baum 			txq->stats.obytes += rte_pktmbuf_data_len(loc->mbuf);
3478256fb0e3SMichael Baum 			if (MLX5_TXOFF_CONFIG(VLAN) &&
3479daa02b5cSOlivier Matz 			    loc->mbuf->ol_flags & RTE_MBUF_F_TX_VLAN)
3480256fb0e3SMichael Baum 				txq->stats.obytes +=
3481256fb0e3SMichael Baum 					sizeof(struct rte_vlan_hdr);
3482256fb0e3SMichael Baum #endif
3483256fb0e3SMichael Baum 		}
3484256fb0e3SMichael Baum 		++loc->pkts_sent;
3485256fb0e3SMichael Baum 		--pkts_n;
3486256fb0e3SMichael Baum 		if (unlikely(!pkts_n || !loc->elts_free || !loc->wqe_free))
3487256fb0e3SMichael Baum 			return MLX5_TXCMP_CODE_EXIT;
3488256fb0e3SMichael Baum 		loc->mbuf = *pkts++;
3489256fb0e3SMichael Baum 		if (pkts_n > 1)
3490256fb0e3SMichael Baum 			rte_prefetch0(*pkts);
3491256fb0e3SMichael Baum 		ret = mlx5_tx_able_to_empw(txq, loc, olx, true);
3492256fb0e3SMichael Baum 		if (unlikely(ret != MLX5_TXCMP_CODE_SINGLE))
3493256fb0e3SMichael Baum 			return ret;
3494256fb0e3SMichael Baum 	}
3495256fb0e3SMichael Baum 	MLX5_ASSERT(false);
3496256fb0e3SMichael Baum }
3497256fb0e3SMichael Baum 
3498256fb0e3SMichael Baum static __rte_always_inline enum mlx5_txcmp_code
3499256fb0e3SMichael Baum mlx5_tx_burst_single(struct mlx5_txq_data *__rte_restrict txq,
3500256fb0e3SMichael Baum 		     struct rte_mbuf **__rte_restrict pkts,
3501256fb0e3SMichael Baum 		     unsigned int pkts_n,
3502256fb0e3SMichael Baum 		     struct mlx5_txq_local *__rte_restrict loc,
3503256fb0e3SMichael Baum 		     unsigned int olx)
3504256fb0e3SMichael Baum {
3505256fb0e3SMichael Baum 	enum mlx5_txcmp_code ret;
3506256fb0e3SMichael Baum 
3507256fb0e3SMichael Baum 	ret = mlx5_tx_able_to_empw(txq, loc, olx, false);
3508256fb0e3SMichael Baum 	if (ret == MLX5_TXCMP_CODE_SINGLE)
3509256fb0e3SMichael Baum 		goto ordinary_send;
3510256fb0e3SMichael Baum 	MLX5_ASSERT(ret == MLX5_TXCMP_CODE_EMPW);
3511256fb0e3SMichael Baum 	for (;;) {
3512256fb0e3SMichael Baum 		/* Optimize for inline/no inline eMPW send. */
3513256fb0e3SMichael Baum 		ret = (MLX5_TXOFF_CONFIG(INLINE)) ?
3514256fb0e3SMichael Baum 			mlx5_tx_burst_empw_inline
3515256fb0e3SMichael Baum 				(txq, pkts, pkts_n, loc, olx) :
3516256fb0e3SMichael Baum 			mlx5_tx_burst_empw_simple
3517256fb0e3SMichael Baum 				(txq, pkts, pkts_n, loc, olx);
3518256fb0e3SMichael Baum 		if (ret != MLX5_TXCMP_CODE_SINGLE)
3519256fb0e3SMichael Baum 			return ret;
3520256fb0e3SMichael Baum 		/* The resources to send one packet should remain. */
3521256fb0e3SMichael Baum 		MLX5_ASSERT(loc->elts_free && loc->wqe_free);
3522256fb0e3SMichael Baum ordinary_send:
3523256fb0e3SMichael Baum 		ret = mlx5_tx_burst_single_send(txq, pkts, pkts_n, loc, olx);
3524256fb0e3SMichael Baum 		MLX5_ASSERT(ret != MLX5_TXCMP_CODE_SINGLE);
3525256fb0e3SMichael Baum 		if (ret != MLX5_TXCMP_CODE_EMPW)
3526256fb0e3SMichael Baum 			return ret;
3527256fb0e3SMichael Baum 		/* The resources to send one packet should remain. */
3528256fb0e3SMichael Baum 		MLX5_ASSERT(loc->elts_free && loc->wqe_free);
3529256fb0e3SMichael Baum 	}
3530256fb0e3SMichael Baum }
3531256fb0e3SMichael Baum 
3532256fb0e3SMichael Baum /**
3533256fb0e3SMichael Baum  * DPDK Tx callback template. This is configured template used to generate
3534256fb0e3SMichael Baum  * routines optimized for specified offload setup.
3535256fb0e3SMichael Baum  * One of this generated functions is chosen at SQ configuration time.
3536256fb0e3SMichael Baum  *
3537256fb0e3SMichael Baum  * @param txq
3538256fb0e3SMichael Baum  *   Generic pointer to TX queue structure.
3539256fb0e3SMichael Baum  * @param[in] pkts
3540256fb0e3SMichael Baum  *   Packets to transmit.
3541256fb0e3SMichael Baum  * @param pkts_n
3542256fb0e3SMichael Baum  *   Number of packets in array.
3543256fb0e3SMichael Baum  * @param olx
3544256fb0e3SMichael Baum  *   Configured offloads mask, presents the bits of MLX5_TXOFF_CONFIG_xxx
3545256fb0e3SMichael Baum  *   values. Should be static to take compile time static configuration
3546256fb0e3SMichael Baum  *   advantages.
3547256fb0e3SMichael Baum  *
3548256fb0e3SMichael Baum  * @return
3549256fb0e3SMichael Baum  *   Number of packets successfully transmitted (<= pkts_n).
3550256fb0e3SMichael Baum  */
3551256fb0e3SMichael Baum static __rte_always_inline uint16_t
3552256fb0e3SMichael Baum mlx5_tx_burst_tmpl(struct mlx5_txq_data *__rte_restrict txq,
3553256fb0e3SMichael Baum 		   struct rte_mbuf **__rte_restrict pkts,
3554256fb0e3SMichael Baum 		   uint16_t pkts_n,
3555256fb0e3SMichael Baum 		   unsigned int olx)
3556256fb0e3SMichael Baum {
3557256fb0e3SMichael Baum 	struct mlx5_txq_local loc;
3558256fb0e3SMichael Baum 	enum mlx5_txcmp_code ret;
3559256fb0e3SMichael Baum 	unsigned int part;
3560256fb0e3SMichael Baum 
3561256fb0e3SMichael Baum 	MLX5_ASSERT(txq->elts_s >= (uint16_t)(txq->elts_head - txq->elts_tail));
3562256fb0e3SMichael Baum 	MLX5_ASSERT(txq->wqe_s >= (uint16_t)(txq->wqe_ci - txq->wqe_pi));
3563256fb0e3SMichael Baum 	if (unlikely(!pkts_n))
3564256fb0e3SMichael Baum 		return 0;
3565256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(INLINE))
3566256fb0e3SMichael Baum 		loc.mbuf_free = 0;
3567256fb0e3SMichael Baum 	loc.pkts_sent = 0;
3568256fb0e3SMichael Baum 	loc.pkts_copy = 0;
3569256fb0e3SMichael Baum 	loc.wqe_last = NULL;
3570256fb0e3SMichael Baum 
3571256fb0e3SMichael Baum send_loop:
3572256fb0e3SMichael Baum 	loc.pkts_loop = loc.pkts_sent;
3573256fb0e3SMichael Baum 	/*
3574256fb0e3SMichael Baum 	 * Check if there are some CQEs, if any:
3575256fb0e3SMichael Baum 	 * - process an encountered errors
3576256fb0e3SMichael Baum 	 * - process the completed WQEs
3577256fb0e3SMichael Baum 	 * - free related mbufs
3578256fb0e3SMichael Baum 	 * - doorbell the NIC about processed CQEs
3579256fb0e3SMichael Baum 	 */
3580256fb0e3SMichael Baum 	rte_prefetch0(*(pkts + loc.pkts_sent));
3581256fb0e3SMichael Baum 	mlx5_tx_handle_completion(txq, olx);
3582256fb0e3SMichael Baum 	/*
3583256fb0e3SMichael Baum 	 * Calculate the number of available resources - elts and WQEs.
3584256fb0e3SMichael Baum 	 * There are two possible different scenarios:
3585256fb0e3SMichael Baum 	 * - no data inlining into WQEs, one WQEBB may contains up to
3586256fb0e3SMichael Baum 	 *   four packets, in this case elts become scarce resource
3587256fb0e3SMichael Baum 	 * - data inlining into WQEs, one packet may require multiple
3588256fb0e3SMichael Baum 	 *   WQEBBs, the WQEs become the limiting factor.
3589256fb0e3SMichael Baum 	 */
3590256fb0e3SMichael Baum 	MLX5_ASSERT(txq->elts_s >= (uint16_t)(txq->elts_head - txq->elts_tail));
3591256fb0e3SMichael Baum 	loc.elts_free = txq->elts_s -
3592256fb0e3SMichael Baum 				(uint16_t)(txq->elts_head - txq->elts_tail);
3593256fb0e3SMichael Baum 	MLX5_ASSERT(txq->wqe_s >= (uint16_t)(txq->wqe_ci - txq->wqe_pi));
3594256fb0e3SMichael Baum 	loc.wqe_free = txq->wqe_s -
3595256fb0e3SMichael Baum 				(uint16_t)(txq->wqe_ci - txq->wqe_pi);
3596256fb0e3SMichael Baum 	if (unlikely(!loc.elts_free || !loc.wqe_free))
3597256fb0e3SMichael Baum 		goto burst_exit;
3598256fb0e3SMichael Baum 	for (;;) {
3599256fb0e3SMichael Baum 		/*
3600256fb0e3SMichael Baum 		 * Fetch the packet from array. Usually this is the first
3601256fb0e3SMichael Baum 		 * packet in series of multi/single segment packets.
3602256fb0e3SMichael Baum 		 */
3603256fb0e3SMichael Baum 		loc.mbuf = *(pkts + loc.pkts_sent);
3604256fb0e3SMichael Baum 		/* Dedicated branch for multi-segment packets. */
3605256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(MULTI) &&
3606256fb0e3SMichael Baum 		    unlikely(NB_SEGS(loc.mbuf) > 1)) {
3607256fb0e3SMichael Baum 			/*
3608256fb0e3SMichael Baum 			 * Multi-segment packet encountered.
3609256fb0e3SMichael Baum 			 * Hardware is able to process it only
3610256fb0e3SMichael Baum 			 * with SEND/TSO opcodes, one packet
3611256fb0e3SMichael Baum 			 * per WQE, do it in dedicated routine.
3612256fb0e3SMichael Baum 			 */
3613256fb0e3SMichael Baum enter_send_multi:
3614256fb0e3SMichael Baum 			MLX5_ASSERT(loc.pkts_sent >= loc.pkts_copy);
3615256fb0e3SMichael Baum 			part = loc.pkts_sent - loc.pkts_copy;
3616256fb0e3SMichael Baum 			if (!MLX5_TXOFF_CONFIG(INLINE) && part) {
3617256fb0e3SMichael Baum 				/*
3618256fb0e3SMichael Baum 				 * There are some single-segment mbufs not
3619256fb0e3SMichael Baum 				 * stored in elts. The mbufs must be in the
3620256fb0e3SMichael Baum 				 * same order as WQEs, so we must copy the
3621256fb0e3SMichael Baum 				 * mbufs to elts here, before the coming
3622256fb0e3SMichael Baum 				 * multi-segment packet mbufs is appended.
3623256fb0e3SMichael Baum 				 */
3624256fb0e3SMichael Baum 				mlx5_tx_copy_elts(txq, pkts + loc.pkts_copy,
3625256fb0e3SMichael Baum 						  part, olx);
3626256fb0e3SMichael Baum 				loc.pkts_copy = loc.pkts_sent;
3627256fb0e3SMichael Baum 			}
3628256fb0e3SMichael Baum 			MLX5_ASSERT(pkts_n > loc.pkts_sent);
3629256fb0e3SMichael Baum 			ret = mlx5_tx_burst_mseg(txq, pkts, pkts_n, &loc, olx);
3630256fb0e3SMichael Baum 			if (!MLX5_TXOFF_CONFIG(INLINE))
3631256fb0e3SMichael Baum 				loc.pkts_copy = loc.pkts_sent;
3632256fb0e3SMichael Baum 			/*
3633256fb0e3SMichael Baum 			 * These returned code checks are supposed
3634256fb0e3SMichael Baum 			 * to be optimized out due to routine inlining.
3635256fb0e3SMichael Baum 			 */
3636256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_EXIT) {
3637256fb0e3SMichael Baum 				/*
3638256fb0e3SMichael Baum 				 * The routine returns this code when
3639256fb0e3SMichael Baum 				 * all packets are sent or there is no
3640256fb0e3SMichael Baum 				 * enough resources to complete request.
3641256fb0e3SMichael Baum 				 */
3642256fb0e3SMichael Baum 				break;
3643256fb0e3SMichael Baum 			}
3644256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_ERROR) {
3645256fb0e3SMichael Baum 				/*
3646256fb0e3SMichael Baum 				 * The routine returns this code when some error
3647256fb0e3SMichael Baum 				 * in the incoming packets format occurred.
3648256fb0e3SMichael Baum 				 */
3649256fb0e3SMichael Baum 				txq->stats.oerrors++;
3650256fb0e3SMichael Baum 				break;
3651256fb0e3SMichael Baum 			}
3652256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_SINGLE) {
3653256fb0e3SMichael Baum 				/*
3654256fb0e3SMichael Baum 				 * The single-segment packet was encountered
3655256fb0e3SMichael Baum 				 * in the array, try to send it with the
3656256fb0e3SMichael Baum 				 * best optimized way, possible engaging eMPW.
3657256fb0e3SMichael Baum 				 */
3658256fb0e3SMichael Baum 				goto enter_send_single;
3659256fb0e3SMichael Baum 			}
3660256fb0e3SMichael Baum 			if (MLX5_TXOFF_CONFIG(TSO) &&
3661256fb0e3SMichael Baum 			    ret == MLX5_TXCMP_CODE_TSO) {
3662256fb0e3SMichael Baum 				/*
3663256fb0e3SMichael Baum 				 * The single-segment TSO packet was
3664256fb0e3SMichael Baum 				 * encountered in the array.
3665256fb0e3SMichael Baum 				 */
3666256fb0e3SMichael Baum 				goto enter_send_tso;
3667256fb0e3SMichael Baum 			}
3668256fb0e3SMichael Baum 			/* We must not get here. Something is going wrong. */
3669256fb0e3SMichael Baum 			MLX5_ASSERT(false);
3670256fb0e3SMichael Baum 			txq->stats.oerrors++;
3671256fb0e3SMichael Baum 			break;
3672256fb0e3SMichael Baum 		}
3673256fb0e3SMichael Baum 		/* Dedicated branch for single-segment TSO packets. */
3674256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(TSO) &&
3675daa02b5cSOlivier Matz 		    unlikely(loc.mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG)) {
3676256fb0e3SMichael Baum 			/*
3677256fb0e3SMichael Baum 			 * TSO might require special way for inlining
3678256fb0e3SMichael Baum 			 * (dedicated parameters) and is sent with
3679256fb0e3SMichael Baum 			 * MLX5_OPCODE_TSO opcode only, provide this
3680256fb0e3SMichael Baum 			 * in dedicated branch.
3681256fb0e3SMichael Baum 			 */
3682256fb0e3SMichael Baum enter_send_tso:
3683256fb0e3SMichael Baum 			MLX5_ASSERT(NB_SEGS(loc.mbuf) == 1);
3684256fb0e3SMichael Baum 			MLX5_ASSERT(pkts_n > loc.pkts_sent);
3685256fb0e3SMichael Baum 			ret = mlx5_tx_burst_tso(txq, pkts, pkts_n, &loc, olx);
3686256fb0e3SMichael Baum 			/*
3687256fb0e3SMichael Baum 			 * These returned code checks are supposed
3688256fb0e3SMichael Baum 			 * to be optimized out due to routine inlining.
3689256fb0e3SMichael Baum 			 */
3690256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_EXIT)
3691256fb0e3SMichael Baum 				break;
3692256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_ERROR) {
3693256fb0e3SMichael Baum 				txq->stats.oerrors++;
3694256fb0e3SMichael Baum 				break;
3695256fb0e3SMichael Baum 			}
3696256fb0e3SMichael Baum 			if (ret == MLX5_TXCMP_CODE_SINGLE)
3697256fb0e3SMichael Baum 				goto enter_send_single;
3698256fb0e3SMichael Baum 			if (MLX5_TXOFF_CONFIG(MULTI) &&
3699256fb0e3SMichael Baum 			    ret == MLX5_TXCMP_CODE_MULTI) {
3700256fb0e3SMichael Baum 				/*
3701256fb0e3SMichael Baum 				 * The multi-segment packet was
3702256fb0e3SMichael Baum 				 * encountered in the array.
3703256fb0e3SMichael Baum 				 */
3704256fb0e3SMichael Baum 				goto enter_send_multi;
3705256fb0e3SMichael Baum 			}
3706256fb0e3SMichael Baum 			/* We must not get here. Something is going wrong. */
3707256fb0e3SMichael Baum 			MLX5_ASSERT(false);
3708256fb0e3SMichael Baum 			txq->stats.oerrors++;
3709256fb0e3SMichael Baum 			break;
3710256fb0e3SMichael Baum 		}
3711256fb0e3SMichael Baum 		/*
3712256fb0e3SMichael Baum 		 * The dedicated branch for the single-segment packets
3713256fb0e3SMichael Baum 		 * without TSO. Often these ones can be sent using
3714256fb0e3SMichael Baum 		 * MLX5_OPCODE_EMPW with multiple packets in one WQE.
3715256fb0e3SMichael Baum 		 * The routine builds the WQEs till it encounters
3716256fb0e3SMichael Baum 		 * the TSO or multi-segment packet (in case if these
3717256fb0e3SMichael Baum 		 * offloads are requested at SQ configuration time).
3718256fb0e3SMichael Baum 		 */
3719256fb0e3SMichael Baum enter_send_single:
3720256fb0e3SMichael Baum 		MLX5_ASSERT(pkts_n > loc.pkts_sent);
3721256fb0e3SMichael Baum 		ret = mlx5_tx_burst_single(txq, pkts, pkts_n, &loc, olx);
3722256fb0e3SMichael Baum 		/*
3723256fb0e3SMichael Baum 		 * These returned code checks are supposed
3724256fb0e3SMichael Baum 		 * to be optimized out due to routine inlining.
3725256fb0e3SMichael Baum 		 */
3726256fb0e3SMichael Baum 		if (ret == MLX5_TXCMP_CODE_EXIT)
3727256fb0e3SMichael Baum 			break;
3728256fb0e3SMichael Baum 		if (ret == MLX5_TXCMP_CODE_ERROR) {
3729256fb0e3SMichael Baum 			txq->stats.oerrors++;
3730256fb0e3SMichael Baum 			break;
3731256fb0e3SMichael Baum 		}
3732256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(MULTI) &&
3733256fb0e3SMichael Baum 		    ret == MLX5_TXCMP_CODE_MULTI) {
3734256fb0e3SMichael Baum 			/*
3735256fb0e3SMichael Baum 			 * The multi-segment packet was
3736256fb0e3SMichael Baum 			 * encountered in the array.
3737256fb0e3SMichael Baum 			 */
3738256fb0e3SMichael Baum 			goto enter_send_multi;
3739256fb0e3SMichael Baum 		}
3740256fb0e3SMichael Baum 		if (MLX5_TXOFF_CONFIG(TSO) &&
3741256fb0e3SMichael Baum 		    ret == MLX5_TXCMP_CODE_TSO) {
3742256fb0e3SMichael Baum 			/*
3743256fb0e3SMichael Baum 			 * The single-segment TSO packet was
3744256fb0e3SMichael Baum 			 * encountered in the array.
3745256fb0e3SMichael Baum 			 */
3746256fb0e3SMichael Baum 			goto enter_send_tso;
3747256fb0e3SMichael Baum 		}
3748256fb0e3SMichael Baum 		/* We must not get here. Something is going wrong. */
3749256fb0e3SMichael Baum 		MLX5_ASSERT(false);
3750256fb0e3SMichael Baum 		txq->stats.oerrors++;
3751256fb0e3SMichael Baum 		break;
3752256fb0e3SMichael Baum 	}
3753256fb0e3SMichael Baum 	/*
3754256fb0e3SMichael Baum 	 * Main Tx loop is completed, do the rest:
3755256fb0e3SMichael Baum 	 * - set completion request if thresholds are reached
3756256fb0e3SMichael Baum 	 * - doorbell the hardware
3757256fb0e3SMichael Baum 	 * - copy the rest of mbufs to elts (if any)
3758256fb0e3SMichael Baum 	 */
3759256fb0e3SMichael Baum 	MLX5_ASSERT(MLX5_TXOFF_CONFIG(INLINE) ||
3760256fb0e3SMichael Baum 		    loc.pkts_sent >= loc.pkts_copy);
3761256fb0e3SMichael Baum 	/* Take a shortcut if nothing is sent. */
3762256fb0e3SMichael Baum 	if (unlikely(loc.pkts_sent == loc.pkts_loop))
3763256fb0e3SMichael Baum 		goto burst_exit;
3764256fb0e3SMichael Baum 	/* Request CQE generation if limits are reached. */
37658fa8d147SViacheslav Ovsiienko 	if (MLX5_TXOFF_CONFIG(TXPP) && __rte_trace_point_fp_is_enabled())
37668fa8d147SViacheslav Ovsiienko 		mlx5_tx_request_completion_trace(txq, &loc, olx);
37678fa8d147SViacheslav Ovsiienko 	else
3768256fb0e3SMichael Baum 		mlx5_tx_request_completion(txq, &loc, olx);
3769256fb0e3SMichael Baum 	/*
3770256fb0e3SMichael Baum 	 * Ring QP doorbell immediately after WQE building completion
3771256fb0e3SMichael Baum 	 * to improve latencies. The pure software related data treatment
3772256fb0e3SMichael Baum 	 * can be completed after doorbell. Tx CQEs for this SQ are
3773256fb0e3SMichael Baum 	 * processed in this thread only by the polling.
3774256fb0e3SMichael Baum 	 *
3775256fb0e3SMichael Baum 	 * The rdma core library can map doorbell register in two ways,
3776256fb0e3SMichael Baum 	 * depending on the environment variable "MLX5_SHUT_UP_BF":
3777256fb0e3SMichael Baum 	 *
3778256fb0e3SMichael Baum 	 * - as regular cached memory, the variable is either missing or
3779256fb0e3SMichael Baum 	 *   set to zero. This type of mapping may cause the significant
3780256fb0e3SMichael Baum 	 *   doorbell register writing latency and requires explicit memory
3781256fb0e3SMichael Baum 	 *   write barrier to mitigate this issue and prevent write combining.
3782256fb0e3SMichael Baum 	 *
3783256fb0e3SMichael Baum 	 * - as non-cached memory, the variable is present and set to not "0"
3784256fb0e3SMichael Baum 	 *   value. This type of mapping may cause performance impact under
3785256fb0e3SMichael Baum 	 *   heavy loading conditions but the explicit write memory barrier is
3786256fb0e3SMichael Baum 	 *   not required and it may improve core performance.
3787256fb0e3SMichael Baum 	 *
3788256fb0e3SMichael Baum 	 * - the legacy behaviour (prior 19.08 release) was to use some
3789256fb0e3SMichael Baum 	 *   heuristics to decide whether write memory barrier should
3790256fb0e3SMichael Baum 	 *   be performed. This behavior is supported with specifying
3791256fb0e3SMichael Baum 	 *   tx_db_nc=2, write barrier is skipped if application provides
3792256fb0e3SMichael Baum 	 *   the full recommended burst of packets, it supposes the next
3793256fb0e3SMichael Baum 	 *   packets are coming and the write barrier will be issued on
3794256fb0e3SMichael Baum 	 *   the next burst (after descriptor writing, at least).
3795256fb0e3SMichael Baum 	 */
37965dfa003dSMichael Baum 	mlx5_doorbell_ring(mlx5_tx_bfreg(txq),
37975dfa003dSMichael Baum 			   *(volatile uint64_t *)loc.wqe_last, txq->wqe_ci,
37985dfa003dSMichael Baum 			   txq->qp_db, !txq->db_nc &&
3799256fb0e3SMichael Baum 			   (!txq->db_heu || pkts_n % MLX5_TX_DEFAULT_BURST));
3800256fb0e3SMichael Baum 	/* Not all of the mbufs may be stored into elts yet. */
3801256fb0e3SMichael Baum 	part = MLX5_TXOFF_CONFIG(INLINE) ? 0 : loc.pkts_sent - loc.pkts_copy;
3802256fb0e3SMichael Baum 	if (!MLX5_TXOFF_CONFIG(INLINE) && part) {
3803256fb0e3SMichael Baum 		/*
3804256fb0e3SMichael Baum 		 * There are some single-segment mbufs not stored in elts.
3805256fb0e3SMichael Baum 		 * It can be only if the last packet was single-segment.
3806256fb0e3SMichael Baum 		 * The copying is gathered into one place due to it is
3807256fb0e3SMichael Baum 		 * a good opportunity to optimize that with SIMD.
3808256fb0e3SMichael Baum 		 * Unfortunately if inlining is enabled the gaps in pointer
3809256fb0e3SMichael Baum 		 * array may happen due to early freeing of the inlined mbufs.
3810256fb0e3SMichael Baum 		 */
3811256fb0e3SMichael Baum 		mlx5_tx_copy_elts(txq, pkts + loc.pkts_copy, part, olx);
3812256fb0e3SMichael Baum 		loc.pkts_copy = loc.pkts_sent;
3813256fb0e3SMichael Baum 	}
3814256fb0e3SMichael Baum 	MLX5_ASSERT(txq->elts_s >= (uint16_t)(txq->elts_head - txq->elts_tail));
3815256fb0e3SMichael Baum 	MLX5_ASSERT(txq->wqe_s >= (uint16_t)(txq->wqe_ci - txq->wqe_pi));
3816256fb0e3SMichael Baum 	if (pkts_n > loc.pkts_sent) {
3817256fb0e3SMichael Baum 		/*
3818256fb0e3SMichael Baum 		 * If burst size is large there might be no enough CQE
3819256fb0e3SMichael Baum 		 * fetched from completion queue and no enough resources
3820256fb0e3SMichael Baum 		 * freed to send all the packets.
3821256fb0e3SMichael Baum 		 */
3822256fb0e3SMichael Baum 		goto send_loop;
3823256fb0e3SMichael Baum 	}
3824256fb0e3SMichael Baum burst_exit:
3825256fb0e3SMichael Baum #ifdef MLX5_PMD_SOFT_COUNTERS
3826256fb0e3SMichael Baum 	/* Increment sent packets counter. */
3827256fb0e3SMichael Baum 	txq->stats.opackets += loc.pkts_sent;
3828256fb0e3SMichael Baum #endif
3829256fb0e3SMichael Baum 	if (MLX5_TXOFF_CONFIG(INLINE) && loc.mbuf_free)
3830256fb0e3SMichael Baum 		__mlx5_tx_free_mbuf(txq, pkts, loc.mbuf_free, olx);
3831a1e910f5SViacheslav Ovsiienko 	/* Trace productive bursts only. */
3832a1e910f5SViacheslav Ovsiienko 	if (__rte_trace_point_fp_is_enabled() && loc.pkts_sent)
3833*02932480STim Martin 		rte_pmd_mlx5_trace_tx_exit(mlx5_read_pcibar_clock_from_txq(txq),
3834*02932480STim Martin 					   loc.pkts_sent, pkts_n);
3835256fb0e3SMichael Baum 	return loc.pkts_sent;
3836256fb0e3SMichael Baum }
3837256fb0e3SMichael Baum 
38381944fbc3SSuanming Mou /**
38391944fbc3SSuanming Mou  * Check whether given TxQ is external.
38401944fbc3SSuanming Mou  *
38411944fbc3SSuanming Mou  * @param dev
38421944fbc3SSuanming Mou  *   Pointer to Ethernet device.
38431944fbc3SSuanming Mou  * @param queue_idx
38441944fbc3SSuanming Mou  *   Tx queue index.
38451944fbc3SSuanming Mou  *
38461944fbc3SSuanming Mou  * @return
38471944fbc3SSuanming Mou  *   True if is external TxQ, otherwise false.
38481944fbc3SSuanming Mou  */
38491944fbc3SSuanming Mou static __rte_always_inline bool
38501944fbc3SSuanming Mou mlx5_is_external_txq(struct rte_eth_dev *dev, uint16_t queue_idx)
38511944fbc3SSuanming Mou {
38521944fbc3SSuanming Mou 	struct mlx5_priv *priv = dev->data->dev_private;
38531944fbc3SSuanming Mou 	struct mlx5_external_q *txq;
38541944fbc3SSuanming Mou 
38551944fbc3SSuanming Mou 	if (!priv->ext_txqs || queue_idx < MLX5_EXTERNAL_TX_QUEUE_ID_MIN)
38561944fbc3SSuanming Mou 		return false;
38571944fbc3SSuanming Mou 	txq = &priv->ext_txqs[queue_idx - MLX5_EXTERNAL_TX_QUEUE_ID_MIN];
38581944fbc3SSuanming Mou 	return !!rte_atomic_load_explicit(&txq->refcnt, rte_memory_order_relaxed);
38591944fbc3SSuanming Mou }
38601944fbc3SSuanming Mou 
3861377b69fbSMichael Baum #endif /* RTE_PMD_MLX5_TX_H_ */
3862