net/mlx5: force inline for completion function
[dpdk.git] / drivers / net / mlx5 / mlx5_rxtx.c
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright 2015 6WIND S.A.
5  *   Copyright 2015 Mellanox.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of 6WIND S.A. nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <assert.h>
35 #include <stdint.h>
36 #include <string.h>
37 #include <stdlib.h>
38
39 /* Verbs header. */
40 /* ISO C doesn't support unnamed structs/unions, disabling -pedantic. */
41 #ifdef PEDANTIC
42 #pragma GCC diagnostic ignored "-pedantic"
43 #endif
44 #include <infiniband/verbs.h>
45 #include <infiniband/mlx5_hw.h>
46 #include <infiniband/arch.h>
47 #ifdef PEDANTIC
48 #pragma GCC diagnostic error "-pedantic"
49 #endif
50
51 /* DPDK headers don't like -pedantic. */
52 #ifdef PEDANTIC
53 #pragma GCC diagnostic ignored "-pedantic"
54 #endif
55 #include <rte_mbuf.h>
56 #include <rte_mempool.h>
57 #include <rte_prefetch.h>
58 #include <rte_common.h>
59 #include <rte_branch_prediction.h>
60 #include <rte_ether.h>
61 #ifdef PEDANTIC
62 #pragma GCC diagnostic error "-pedantic"
63 #endif
64
65 #include "mlx5.h"
66 #include "mlx5_utils.h"
67 #include "mlx5_rxtx.h"
68 #include "mlx5_autoconf.h"
69 #include "mlx5_defs.h"
70 #include "mlx5_prm.h"
71
72 #ifndef NDEBUG
73
74 /**
75  * Verify or set magic value in CQE.
76  *
77  * @param cqe
78  *   Pointer to CQE.
79  *
80  * @return
81  *   0 the first time.
82  */
83 static inline int
84 check_cqe64_seen(volatile struct mlx5_cqe64 *cqe)
85 {
86         static const uint8_t magic[] = "seen";
87         volatile uint8_t (*buf)[sizeof(cqe->rsvd40)] = &cqe->rsvd40;
88         int ret = 1;
89         unsigned int i;
90
91         for (i = 0; i < sizeof(magic) && i < sizeof(*buf); ++i)
92                 if (!ret || (*buf)[i] != magic[i]) {
93                         ret = 0;
94                         (*buf)[i] = magic[i];
95                 }
96         return ret;
97 }
98
99 #endif /* NDEBUG */
100
101 static inline int
102 check_cqe64(volatile struct mlx5_cqe64 *cqe,
103             unsigned int cqes_n, const uint16_t ci)
104             __attribute__((always_inline));
105
106 /**
107  * Check whether CQE is valid.
108  *
109  * @param cqe
110  *   Pointer to CQE.
111  * @param cqes_n
112  *   Size of completion queue.
113  * @param ci
114  *   Consumer index.
115  *
116  * @return
117  *   0 on success, 1 on failure.
118  */
119 static inline int
120 check_cqe64(volatile struct mlx5_cqe64 *cqe,
121                 unsigned int cqes_n, const uint16_t ci)
122 {
123         uint16_t idx = ci & cqes_n;
124         uint8_t op_own = cqe->op_own;
125         uint8_t op_owner = MLX5_CQE_OWNER(op_own);
126         uint8_t op_code = MLX5_CQE_OPCODE(op_own);
127
128         if (unlikely((op_owner != (!!(idx))) || (op_code == MLX5_CQE_INVALID)))
129                 return 1; /* No CQE. */
130 #ifndef NDEBUG
131         if ((op_code == MLX5_CQE_RESP_ERR) ||
132             (op_code == MLX5_CQE_REQ_ERR)) {
133                 volatile struct mlx5_err_cqe *err_cqe = (volatile void *)cqe;
134                 uint8_t syndrome = err_cqe->syndrome;
135
136                 if ((syndrome == MLX5_CQE_SYNDROME_LOCAL_LENGTH_ERR) ||
137                     (syndrome == MLX5_CQE_SYNDROME_REMOTE_ABORTED_ERR))
138                         return 0;
139                 if (!check_cqe64_seen(cqe))
140                         ERROR("unexpected CQE error %u (0x%02x)"
141                               " syndrome 0x%02x",
142                               op_code, op_code, syndrome);
143                 return 1;
144         } else if ((op_code != MLX5_CQE_RESP_SEND) &&
145                    (op_code != MLX5_CQE_REQ)) {
146                 if (!check_cqe64_seen(cqe))
147                         ERROR("unexpected CQE opcode %u (0x%02x)",
148                               op_code, op_code);
149                 return 1;
150         }
151 #endif /* NDEBUG */
152         return 0;
153 }
154
155 static inline void
156 txq_complete(struct txq *txq) __attribute__((always_inline));
157
158 /**
159  * Manage TX completions.
160  *
161  * When sending a burst, mlx5_tx_burst() posts several WRs.
162  *
163  * @param txq
164  *   Pointer to TX queue structure.
165  */
166 static inline void
167 txq_complete(struct txq *txq)
168 {
169         const unsigned int elts_n = txq->elts_n;
170         const unsigned int cqe_n = txq->cqe_n;
171         const unsigned int cqe_cnt = cqe_n - 1;
172         uint16_t elts_free = txq->elts_tail;
173         uint16_t elts_tail;
174         uint16_t cq_ci = txq->cq_ci;
175         volatile struct mlx5_cqe64 *cqe = NULL;
176         volatile union mlx5_wqe *wqe;
177
178         do {
179                 volatile struct mlx5_cqe64 *tmp;
180
181                 tmp = &(*txq->cqes)[cq_ci & cqe_cnt].cqe64;
182                 if (check_cqe64(tmp, cqe_n, cq_ci))
183                         break;
184                 cqe = tmp;
185 #ifndef NDEBUG
186                 if (MLX5_CQE_FORMAT(cqe->op_own) == MLX5_COMPRESSED) {
187                         if (!check_cqe64_seen(cqe))
188                                 ERROR("unexpected compressed CQE, TX stopped");
189                         return;
190                 }
191                 if ((MLX5_CQE_OPCODE(cqe->op_own) == MLX5_CQE_RESP_ERR) ||
192                     (MLX5_CQE_OPCODE(cqe->op_own) == MLX5_CQE_REQ_ERR)) {
193                         if (!check_cqe64_seen(cqe))
194                                 ERROR("unexpected error CQE, TX stopped");
195                         return;
196                 }
197 #endif /* NDEBUG */
198                 ++cq_ci;
199         } while (1);
200         if (unlikely(cqe == NULL))
201                 return;
202         wqe = &(*txq->wqes)[htons(cqe->wqe_counter) & (txq->wqe_n - 1)];
203         elts_tail = wqe->wqe.ctrl.data[3];
204         assert(elts_tail < txq->wqe_n);
205         /* Free buffers. */
206         while (elts_free != elts_tail) {
207                 struct rte_mbuf *elt = (*txq->elts)[elts_free];
208                 unsigned int elts_free_next =
209                         (elts_free + 1) & (elts_n - 1);
210                 struct rte_mbuf *elt_next = (*txq->elts)[elts_free_next];
211
212 #ifndef NDEBUG
213                 /* Poisoning. */
214                 memset(&(*txq->elts)[elts_free],
215                        0x66,
216                        sizeof((*txq->elts)[elts_free]));
217 #endif
218                 RTE_MBUF_PREFETCH_TO_FREE(elt_next);
219                 /* Only one segment needs to be freed. */
220                 rte_pktmbuf_free_seg(elt);
221                 elts_free = elts_free_next;
222         }
223         txq->cq_ci = cq_ci;
224         txq->elts_tail = elts_tail;
225         /* Update the consumer index. */
226         rte_wmb();
227         *txq->cq_db = htonl(cq_ci);
228 }
229
230 /**
231  * Get Memory Pool (MP) from mbuf. If mbuf is indirect, the pool from which
232  * the cloned mbuf is allocated is returned instead.
233  *
234  * @param buf
235  *   Pointer to mbuf.
236  *
237  * @return
238  *   Memory pool where data is located for given mbuf.
239  */
240 static struct rte_mempool *
241 txq_mb2mp(struct rte_mbuf *buf)
242 {
243         if (unlikely(RTE_MBUF_INDIRECT(buf)))
244                 return rte_mbuf_from_indirect(buf)->pool;
245         return buf->pool;
246 }
247
248 static inline uint32_t
249 txq_mp2mr(struct txq *txq, struct rte_mempool *mp)
250         __attribute__((always_inline));
251
252 /**
253  * Get Memory Region (MR) <-> Memory Pool (MP) association from txq->mp2mr[].
254  * Add MP to txq->mp2mr[] if it's not registered yet. If mp2mr[] is full,
255  * remove an entry first.
256  *
257  * @param txq
258  *   Pointer to TX queue structure.
259  * @param[in] mp
260  *   Memory Pool for which a Memory Region lkey must be returned.
261  *
262  * @return
263  *   mr->lkey on success, (uint32_t)-1 on failure.
264  */
265 static inline uint32_t
266 txq_mp2mr(struct txq *txq, struct rte_mempool *mp)
267 {
268         unsigned int i;
269         uint32_t lkey = (uint32_t)-1;
270
271         for (i = 0; (i != RTE_DIM(txq->mp2mr)); ++i) {
272                 if (unlikely(txq->mp2mr[i].mp == NULL)) {
273                         /* Unknown MP, add a new MR for it. */
274                         break;
275                 }
276                 if (txq->mp2mr[i].mp == mp) {
277                         assert(txq->mp2mr[i].lkey != (uint32_t)-1);
278                         assert(htonl(txq->mp2mr[i].mr->lkey) ==
279                                txq->mp2mr[i].lkey);
280                         lkey = txq->mp2mr[i].lkey;
281                         break;
282                 }
283         }
284         if (unlikely(lkey == (uint32_t)-1))
285                 lkey = txq_mp2mr_reg(txq, mp, i);
286         return lkey;
287 }
288
289 /**
290  * Write a regular WQE.
291  *
292  * @param txq
293  *   Pointer to TX queue structure.
294  * @param wqe
295  *   Pointer to the WQE to fill.
296  * @param addr
297  *   Buffer data address.
298  * @param length
299  *   Packet length.
300  * @param lkey
301  *   Memory region lkey.
302  */
303 static inline void
304 mlx5_wqe_write(struct txq *txq, volatile union mlx5_wqe *wqe,
305                uintptr_t addr, uint32_t length, uint32_t lkey)
306 {
307         wqe->wqe.ctrl.data[0] = htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND);
308         wqe->wqe.ctrl.data[1] = htonl((txq->qp_num_8s) | 4);
309         wqe->wqe.ctrl.data[2] = 0;
310         wqe->wqe.ctrl.data[3] = 0;
311         wqe->inl.eseg.rsvd0 = 0;
312         wqe->inl.eseg.rsvd1 = 0;
313         wqe->inl.eseg.mss = 0;
314         wqe->inl.eseg.rsvd2 = 0;
315         wqe->wqe.eseg.inline_hdr_sz = htons(MLX5_ETH_INLINE_HEADER_SIZE);
316         /* Copy the first 16 bytes into inline header. */
317         rte_memcpy((uint8_t *)(uintptr_t)wqe->wqe.eseg.inline_hdr_start,
318                    (uint8_t *)(uintptr_t)addr,
319                    MLX5_ETH_INLINE_HEADER_SIZE);
320         addr += MLX5_ETH_INLINE_HEADER_SIZE;
321         length -= MLX5_ETH_INLINE_HEADER_SIZE;
322         /* Store remaining data in data segment. */
323         wqe->wqe.dseg.byte_count = htonl(length);
324         wqe->wqe.dseg.lkey = lkey;
325         wqe->wqe.dseg.addr = htonll(addr);
326         /* Increment consumer index. */
327         ++txq->wqe_ci;
328 }
329
330 /**
331  * Write a regular WQE with VLAN.
332  *
333  * @param txq
334  *   Pointer to TX queue structure.
335  * @param wqe
336  *   Pointer to the WQE to fill.
337  * @param addr
338  *   Buffer data address.
339  * @param length
340  *   Packet length.
341  * @param lkey
342  *   Memory region lkey.
343  * @param vlan_tci
344  *   VLAN field to insert in packet.
345  */
346 static inline void
347 mlx5_wqe_write_vlan(struct txq *txq, volatile union mlx5_wqe *wqe,
348                     uintptr_t addr, uint32_t length, uint32_t lkey,
349                     uint16_t vlan_tci)
350 {
351         uint32_t vlan = htonl(0x81000000 | vlan_tci);
352
353         wqe->wqe.ctrl.data[0] = htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND);
354         wqe->wqe.ctrl.data[1] = htonl((txq->qp_num_8s) | 4);
355         wqe->wqe.ctrl.data[2] = 0;
356         wqe->wqe.ctrl.data[3] = 0;
357         wqe->inl.eseg.rsvd0 = 0;
358         wqe->inl.eseg.rsvd1 = 0;
359         wqe->inl.eseg.mss = 0;
360         wqe->inl.eseg.rsvd2 = 0;
361         wqe->wqe.eseg.inline_hdr_sz = htons(MLX5_ETH_VLAN_INLINE_HEADER_SIZE);
362         /*
363          * Copy 12 bytes of source & destination MAC address.
364          * Copy 4 bytes of VLAN.
365          * Copy 2 bytes of Ether type.
366          */
367         rte_memcpy((uint8_t *)(uintptr_t)wqe->wqe.eseg.inline_hdr_start,
368                    (uint8_t *)(uintptr_t)addr, 12);
369         rte_memcpy((uint8_t *)((uintptr_t)wqe->wqe.eseg.inline_hdr_start + 12),
370                    &vlan, sizeof(vlan));
371         rte_memcpy((uint8_t *)((uintptr_t)wqe->wqe.eseg.inline_hdr_start + 16),
372                    (uint8_t *)((uintptr_t)addr + 12), 2);
373         addr += MLX5_ETH_VLAN_INLINE_HEADER_SIZE - sizeof(vlan);
374         length -= MLX5_ETH_VLAN_INLINE_HEADER_SIZE - sizeof(vlan);
375         /* Store remaining data in data segment. */
376         wqe->wqe.dseg.byte_count = htonl(length);
377         wqe->wqe.dseg.lkey = lkey;
378         wqe->wqe.dseg.addr = htonll(addr);
379         /* Increment consumer index. */
380         ++txq->wqe_ci;
381 }
382
383 /**
384  * Write a inline WQE.
385  *
386  * @param txq
387  *   Pointer to TX queue structure.
388  * @param wqe
389  *   Pointer to the WQE to fill.
390  * @param addr
391  *   Buffer data address.
392  * @param length
393  *   Packet length.
394  * @param lkey
395  *   Memory region lkey.
396  */
397 static inline void
398 mlx5_wqe_write_inline(struct txq *txq, volatile union mlx5_wqe *wqe,
399                       uintptr_t addr, uint32_t length)
400 {
401         uint32_t size;
402         uint16_t wqe_cnt = txq->wqe_n - 1;
403         uint16_t wqe_ci = txq->wqe_ci + 1;
404
405         /* Copy the first 16 bytes into inline header. */
406         rte_memcpy((void *)(uintptr_t)wqe->inl.eseg.inline_hdr_start,
407                    (void *)(uintptr_t)addr,
408                    MLX5_ETH_INLINE_HEADER_SIZE);
409         addr += MLX5_ETH_INLINE_HEADER_SIZE;
410         length -= MLX5_ETH_INLINE_HEADER_SIZE;
411         size = 3 + ((4 + length + 15) / 16);
412         wqe->inl.byte_cnt = htonl(length | MLX5_INLINE_SEG);
413         rte_memcpy((void *)(uintptr_t)&wqe->inl.data[0],
414                    (void *)addr, MLX5_WQE64_INL_DATA);
415         addr += MLX5_WQE64_INL_DATA;
416         length -= MLX5_WQE64_INL_DATA;
417         while (length) {
418                 volatile union mlx5_wqe *wqe_next =
419                         &(*txq->wqes)[wqe_ci & wqe_cnt];
420                 uint32_t copy_bytes = (length > sizeof(*wqe)) ?
421                                       sizeof(*wqe) :
422                                       length;
423
424                 rte_mov64((uint8_t *)(uintptr_t)&wqe_next->data[0],
425                           (uint8_t *)addr);
426                 addr += copy_bytes;
427                 length -= copy_bytes;
428                 ++wqe_ci;
429         }
430         assert(size < 64);
431         wqe->inl.ctrl.data[0] = htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND);
432         wqe->inl.ctrl.data[1] = htonl(txq->qp_num_8s | size);
433         wqe->inl.ctrl.data[2] = 0;
434         wqe->inl.ctrl.data[3] = 0;
435         wqe->inl.eseg.rsvd0 = 0;
436         wqe->inl.eseg.rsvd1 = 0;
437         wqe->inl.eseg.mss = 0;
438         wqe->inl.eseg.rsvd2 = 0;
439         wqe->inl.eseg.inline_hdr_sz = htons(MLX5_ETH_INLINE_HEADER_SIZE);
440         /* Increment consumer index. */
441         txq->wqe_ci = wqe_ci;
442 }
443
444 /**
445  * Write a inline WQE with VLAN.
446  *
447  * @param txq
448  *   Pointer to TX queue structure.
449  * @param wqe
450  *   Pointer to the WQE to fill.
451  * @param addr
452  *   Buffer data address.
453  * @param length
454  *   Packet length.
455  * @param lkey
456  *   Memory region lkey.
457  * @param vlan_tci
458  *   VLAN field to insert in packet.
459  */
460 static inline void
461 mlx5_wqe_write_inline_vlan(struct txq *txq, volatile union mlx5_wqe *wqe,
462                            uintptr_t addr, uint32_t length, uint16_t vlan_tci)
463 {
464         uint32_t size;
465         uint32_t wqe_cnt = txq->wqe_n - 1;
466         uint16_t wqe_ci = txq->wqe_ci + 1;
467         uint32_t vlan = htonl(0x81000000 | vlan_tci);
468
469         /*
470          * Copy 12 bytes of source & destination MAC address.
471          * Copy 4 bytes of VLAN.
472          * Copy 2 bytes of Ether type.
473          */
474         rte_memcpy((uint8_t *)(uintptr_t)wqe->inl.eseg.inline_hdr_start,
475                    (uint8_t *)addr, 12);
476         rte_memcpy((uint8_t *)(uintptr_t)wqe->inl.eseg.inline_hdr_start + 12,
477                    &vlan, sizeof(vlan));
478         rte_memcpy((uint8_t *)((uintptr_t)wqe->inl.eseg.inline_hdr_start + 16),
479                    (uint8_t *)(addr + 12), 2);
480         addr += MLX5_ETH_VLAN_INLINE_HEADER_SIZE - sizeof(vlan);
481         length -= MLX5_ETH_VLAN_INLINE_HEADER_SIZE - sizeof(vlan);
482         size = (sizeof(wqe->inl.ctrl.ctrl) +
483                 sizeof(wqe->inl.eseg) +
484                 sizeof(wqe->inl.byte_cnt) +
485                 length + 15) / 16;
486         wqe->inl.byte_cnt = htonl(length | MLX5_INLINE_SEG);
487         rte_memcpy((void *)(uintptr_t)&wqe->inl.data[0],
488                    (void *)addr, MLX5_WQE64_INL_DATA);
489         addr += MLX5_WQE64_INL_DATA;
490         length -= MLX5_WQE64_INL_DATA;
491         while (length) {
492                 volatile union mlx5_wqe *wqe_next =
493                         &(*txq->wqes)[wqe_ci & wqe_cnt];
494                 uint32_t copy_bytes = (length > sizeof(*wqe)) ?
495                                       sizeof(*wqe) :
496                                       length;
497
498                 rte_mov64((uint8_t *)(uintptr_t)&wqe_next->data[0],
499                           (uint8_t *)addr);
500                 addr += copy_bytes;
501                 length -= copy_bytes;
502                 ++wqe_ci;
503         }
504         assert(size < 64);
505         wqe->inl.ctrl.data[0] = htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND);
506         wqe->inl.ctrl.data[1] = htonl(txq->qp_num_8s | size);
507         wqe->inl.ctrl.data[2] = 0;
508         wqe->inl.ctrl.data[3] = 0;
509         wqe->inl.eseg.rsvd0 = 0;
510         wqe->inl.eseg.rsvd1 = 0;
511         wqe->inl.eseg.mss = 0;
512         wqe->inl.eseg.rsvd2 = 0;
513         wqe->inl.eseg.inline_hdr_sz = htons(MLX5_ETH_VLAN_INLINE_HEADER_SIZE);
514         /* Increment consumer index. */
515         txq->wqe_ci = wqe_ci;
516 }
517
518 /**
519  * Ring TX queue doorbell.
520  *
521  * @param txq
522  *   Pointer to TX queue structure.
523  */
524 static inline void
525 mlx5_tx_dbrec(struct txq *txq)
526 {
527         uint8_t *dst = (uint8_t *)((uintptr_t)txq->bf_reg + txq->bf_offset);
528         uint32_t data[4] = {
529                 htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND),
530                 htonl(txq->qp_num_8s),
531                 0,
532                 0,
533         };
534         rte_wmb();
535         *txq->qp_db = htonl(txq->wqe_ci);
536         /* Ensure ordering between DB record and BF copy. */
537         rte_wmb();
538         rte_mov16(dst, (uint8_t *)data);
539         txq->bf_offset ^= txq->bf_buf_size;
540 }
541
542 /**
543  * Prefetch a CQE.
544  *
545  * @param txq
546  *   Pointer to TX queue structure.
547  * @param cqe_ci
548  *   CQE consumer index.
549  */
550 static inline void
551 tx_prefetch_cqe(struct txq *txq, uint16_t ci)
552 {
553         volatile struct mlx5_cqe64 *cqe;
554
555         cqe = &(*txq->cqes)[ci & (txq->cqe_n - 1)].cqe64;
556         rte_prefetch0(cqe);
557 }
558
559 /**
560  * Prefetch a WQE.
561  *
562  * @param txq
563  *   Pointer to TX queue structure.
564  * @param  wqe_ci
565  *   WQE consumer index.
566  */
567 static inline void
568 tx_prefetch_wqe(struct txq *txq, uint16_t ci)
569 {
570         volatile union mlx5_wqe *wqe;
571
572         wqe = &(*txq->wqes)[ci & (txq->wqe_n - 1)];
573         rte_prefetch0(wqe);
574 }
575
576 /**
577  * DPDK callback for TX.
578  *
579  * @param dpdk_txq
580  *   Generic pointer to TX queue structure.
581  * @param[in] pkts
582  *   Packets to transmit.
583  * @param pkts_n
584  *   Number of packets in array.
585  *
586  * @return
587  *   Number of packets successfully transmitted (<= pkts_n).
588  */
589 uint16_t
590 mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
591 {
592         struct txq *txq = (struct txq *)dpdk_txq;
593         uint16_t elts_head = txq->elts_head;
594         const unsigned int elts_n = txq->elts_n;
595         unsigned int i = 0;
596         unsigned int j = 0;
597         unsigned int max;
598         unsigned int comp;
599         volatile union mlx5_wqe *wqe = NULL;
600
601         if (unlikely(!pkts_n))
602                 return 0;
603         /* Prefetch first packet cacheline. */
604         tx_prefetch_cqe(txq, txq->cq_ci);
605         tx_prefetch_cqe(txq, txq->cq_ci + 1);
606         rte_prefetch0(*pkts);
607         /* Start processing. */
608         txq_complete(txq);
609         max = (elts_n - (elts_head - txq->elts_tail));
610         if (max > elts_n)
611                 max -= elts_n;
612         do {
613                 struct rte_mbuf *buf = *(pkts++);
614                 unsigned int elts_head_next;
615                 uintptr_t addr;
616                 uint32_t length;
617                 uint32_t lkey;
618                 unsigned int segs_n = buf->nb_segs;
619                 volatile struct mlx5_wqe_data_seg *dseg;
620                 unsigned int ds = sizeof(*wqe) / 16;
621
622                 /*
623                  * Make sure there is enough room to store this packet and
624                  * that one ring entry remains unused.
625                  */
626                 assert(segs_n);
627                 if (max < segs_n + 1)
628                         break;
629                 max -= segs_n;
630                 --pkts_n;
631                 elts_head_next = (elts_head + 1) & (elts_n - 1);
632                 wqe = &(*txq->wqes)[txq->wqe_ci & (txq->wqe_n - 1)];
633                 dseg = &wqe->wqe.dseg;
634                 rte_prefetch0(wqe);
635                 if (pkts_n)
636                         rte_prefetch0(*pkts);
637                 /* Retrieve buffer information. */
638                 addr = rte_pktmbuf_mtod(buf, uintptr_t);
639                 length = DATA_LEN(buf);
640                 /* Update element. */
641                 (*txq->elts)[elts_head] = buf;
642                 /* Prefetch next buffer data. */
643                 if (pkts_n)
644                         rte_prefetch0(rte_pktmbuf_mtod(*pkts,
645                                                        volatile void *));
646                 /* Retrieve Memory Region key for this memory pool. */
647                 lkey = txq_mp2mr(txq, txq_mb2mp(buf));
648                 if (buf->ol_flags & PKT_TX_VLAN_PKT)
649                         mlx5_wqe_write_vlan(txq, wqe, addr, length, lkey,
650                                             buf->vlan_tci);
651                 else
652                         mlx5_wqe_write(txq, wqe, addr, length, lkey);
653                 /* Should we enable HW CKSUM offload */
654                 if (buf->ol_flags &
655                     (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM)) {
656                         wqe->wqe.eseg.cs_flags =
657                                 MLX5_ETH_WQE_L3_CSUM |
658                                 MLX5_ETH_WQE_L4_CSUM;
659                 } else {
660                         wqe->wqe.eseg.cs_flags = 0;
661                 }
662                 while (--segs_n) {
663                         /*
664                          * Spill on next WQE when the current one does not have
665                          * enough room left. Size of WQE must a be a multiple
666                          * of data segment size.
667                          */
668                         assert(!(sizeof(*wqe) % sizeof(*dseg)));
669                         if (!(ds % (sizeof(*wqe) / 16)))
670                                 dseg = (volatile void *)
671                                         &(*txq->wqes)[txq->wqe_ci++ &
672                                                       (txq->wqe_n - 1)];
673                         else
674                                 ++dseg;
675                         ++ds;
676                         buf = buf->next;
677                         assert(buf);
678                         /* Store segment information. */
679                         dseg->byte_count = htonl(DATA_LEN(buf));
680                         dseg->lkey = txq_mp2mr(txq, txq_mb2mp(buf));
681                         dseg->addr = htonll(rte_pktmbuf_mtod(buf, uintptr_t));
682                         (*txq->elts)[elts_head_next] = buf;
683                         elts_head_next = (elts_head_next + 1) & (elts_n - 1);
684 #ifdef MLX5_PMD_SOFT_COUNTERS
685                         length += DATA_LEN(buf);
686 #endif
687                         ++j;
688                 }
689                 /* Update DS field in WQE. */
690                 wqe->wqe.ctrl.data[1] &= htonl(0xffffffc0);
691                 wqe->wqe.ctrl.data[1] |= htonl(ds & 0x3f);
692                 elts_head = elts_head_next;
693 #ifdef MLX5_PMD_SOFT_COUNTERS
694                 /* Increment sent bytes counter. */
695                 txq->stats.obytes += length;
696 #endif
697                 elts_head = elts_head_next;
698                 ++i;
699         } while (pkts_n);
700         /* Take a shortcut if nothing must be sent. */
701         if (unlikely(i == 0))
702                 return 0;
703         /* Check whether completion threshold has been reached. */
704         comp = txq->elts_comp + i + j;
705         if (comp >= MLX5_TX_COMP_THRESH) {
706                 /* Request completion on last WQE. */
707                 wqe->wqe.ctrl.data[2] = htonl(8);
708                 /* Save elts_head in unused "immediate" field of WQE. */
709                 wqe->wqe.ctrl.data[3] = elts_head;
710                 txq->elts_comp = 0;
711         } else {
712                 txq->elts_comp = comp;
713         }
714 #ifdef MLX5_PMD_SOFT_COUNTERS
715         /* Increment sent packets counter. */
716         txq->stats.opackets += i;
717 #endif
718         /* Ring QP doorbell. */
719         mlx5_tx_dbrec(txq);
720         txq->elts_head = elts_head;
721         return i;
722 }
723
724 /**
725  * DPDK callback for TX with inline support.
726  *
727  * @param dpdk_txq
728  *   Generic pointer to TX queue structure.
729  * @param[in] pkts
730  *   Packets to transmit.
731  * @param pkts_n
732  *   Number of packets in array.
733  *
734  * @return
735  *   Number of packets successfully transmitted (<= pkts_n).
736  */
737 uint16_t
738 mlx5_tx_burst_inline(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
739 {
740         struct txq *txq = (struct txq *)dpdk_txq;
741         uint16_t elts_head = txq->elts_head;
742         const unsigned int elts_n = txq->elts_n;
743         unsigned int i = 0;
744         unsigned int j = 0;
745         unsigned int max;
746         unsigned int comp;
747         volatile union mlx5_wqe *wqe = NULL;
748         unsigned int max_inline = txq->max_inline;
749
750         if (unlikely(!pkts_n))
751                 return 0;
752         /* Prefetch first packet cacheline. */
753         tx_prefetch_cqe(txq, txq->cq_ci);
754         tx_prefetch_cqe(txq, txq->cq_ci + 1);
755         rte_prefetch0(*pkts);
756         /* Start processing. */
757         txq_complete(txq);
758         max = (elts_n - (elts_head - txq->elts_tail));
759         if (max > elts_n)
760                 max -= elts_n;
761         do {
762                 struct rte_mbuf *buf = *(pkts++);
763                 unsigned int elts_head_next;
764                 uintptr_t addr;
765                 uint32_t length;
766                 uint32_t lkey;
767                 unsigned int segs_n = buf->nb_segs;
768                 volatile struct mlx5_wqe_data_seg *dseg;
769                 unsigned int ds = sizeof(*wqe) / 16;
770
771                 /*
772                  * Make sure there is enough room to store this packet and
773                  * that one ring entry remains unused.
774                  */
775                 assert(segs_n);
776                 if (max < segs_n + 1)
777                         break;
778                 max -= segs_n;
779                 --pkts_n;
780                 elts_head_next = (elts_head + 1) & (elts_n - 1);
781                 wqe = &(*txq->wqes)[txq->wqe_ci & (txq->wqe_n - 1)];
782                 dseg = &wqe->wqe.dseg;
783                 tx_prefetch_wqe(txq, txq->wqe_ci);
784                 tx_prefetch_wqe(txq, txq->wqe_ci + 1);
785                 if (pkts_n)
786                         rte_prefetch0(*pkts);
787                 /* Should we enable HW CKSUM offload */
788                 if (buf->ol_flags &
789                     (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM)) {
790                         wqe->inl.eseg.cs_flags =
791                                 MLX5_ETH_WQE_L3_CSUM |
792                                 MLX5_ETH_WQE_L4_CSUM;
793                 } else {
794                         wqe->inl.eseg.cs_flags = 0;
795                 }
796                 /* Retrieve buffer information. */
797                 addr = rte_pktmbuf_mtod(buf, uintptr_t);
798                 length = DATA_LEN(buf);
799                 /* Update element. */
800                 (*txq->elts)[elts_head] = buf;
801                 /* Prefetch next buffer data. */
802                 if (pkts_n)
803                         rte_prefetch0(rte_pktmbuf_mtod(*pkts,
804                                                        volatile void *));
805                 if ((length <= max_inline) && (segs_n == 1)) {
806                         if (buf->ol_flags & PKT_TX_VLAN_PKT)
807                                 mlx5_wqe_write_inline_vlan(txq, wqe,
808                                                            addr, length,
809                                                            buf->vlan_tci);
810                         else
811                                 mlx5_wqe_write_inline(txq, wqe, addr, length);
812                         goto skip_segs;
813                 } else {
814                         /* Retrieve Memory Region key for this memory pool. */
815                         lkey = txq_mp2mr(txq, txq_mb2mp(buf));
816                         if (buf->ol_flags & PKT_TX_VLAN_PKT)
817                                 mlx5_wqe_write_vlan(txq, wqe, addr, length,
818                                                     lkey, buf->vlan_tci);
819                         else
820                                 mlx5_wqe_write(txq, wqe, addr, length, lkey);
821                 }
822                 while (--segs_n) {
823                         /*
824                          * Spill on next WQE when the current one does not have
825                          * enough room left. Size of WQE must a be a multiple
826                          * of data segment size.
827                          */
828                         assert(!(sizeof(*wqe) % sizeof(*dseg)));
829                         if (!(ds % (sizeof(*wqe) / 16)))
830                                 dseg = (volatile void *)
831                                         &(*txq->wqes)[txq->wqe_ci++ &
832                                                       (txq->wqe_n - 1)];
833                         else
834                                 ++dseg;
835                         ++ds;
836                         buf = buf->next;
837                         assert(buf);
838                         /* Store segment information. */
839                         dseg->byte_count = htonl(DATA_LEN(buf));
840                         dseg->lkey = txq_mp2mr(txq, txq_mb2mp(buf));
841                         dseg->addr = htonll(rte_pktmbuf_mtod(buf, uintptr_t));
842                         (*txq->elts)[elts_head_next] = buf;
843                         elts_head_next = (elts_head_next + 1) & (elts_n - 1);
844 #ifdef MLX5_PMD_SOFT_COUNTERS
845                         length += DATA_LEN(buf);
846 #endif
847                         ++j;
848                 }
849                 /* Update DS field in WQE. */
850                 wqe->inl.ctrl.data[1] &= htonl(0xffffffc0);
851                 wqe->inl.ctrl.data[1] |= htonl(ds & 0x3f);
852 skip_segs:
853                 elts_head = elts_head_next;
854 #ifdef MLX5_PMD_SOFT_COUNTERS
855                 /* Increment sent bytes counter. */
856                 txq->stats.obytes += length;
857 #endif
858                 ++i;
859         } while (pkts_n);
860         /* Take a shortcut if nothing must be sent. */
861         if (unlikely(i == 0))
862                 return 0;
863         /* Check whether completion threshold has been reached. */
864         comp = txq->elts_comp + i + j;
865         if (comp >= MLX5_TX_COMP_THRESH) {
866                 /* Request completion on last WQE. */
867                 wqe->inl.ctrl.data[2] = htonl(8);
868                 /* Save elts_head in unused "immediate" field of WQE. */
869                 wqe->inl.ctrl.data[3] = elts_head;
870                 txq->elts_comp = 0;
871         } else {
872                 txq->elts_comp = comp;
873         }
874 #ifdef MLX5_PMD_SOFT_COUNTERS
875         /* Increment sent packets counter. */
876         txq->stats.opackets += i;
877 #endif
878         /* Ring QP doorbell. */
879         mlx5_tx_dbrec(txq);
880         txq->elts_head = elts_head;
881         return i;
882 }
883
884 /**
885  * Open a MPW session.
886  *
887  * @param txq
888  *   Pointer to TX queue structure.
889  * @param mpw
890  *   Pointer to MPW session structure.
891  * @param length
892  *   Packet length.
893  */
894 static inline void
895 mlx5_mpw_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
896 {
897         uint16_t idx = txq->wqe_ci & (txq->wqe_n - 1);
898         volatile struct mlx5_wqe_data_seg (*dseg)[MLX5_MPW_DSEG_MAX] =
899                 (volatile struct mlx5_wqe_data_seg (*)[])
900                 (uintptr_t)&(*txq->wqes)[(idx + 1) & (txq->wqe_n - 1)];
901
902         mpw->state = MLX5_MPW_STATE_OPENED;
903         mpw->pkts_n = 0;
904         mpw->len = length;
905         mpw->total_len = 0;
906         mpw->wqe = &(*txq->wqes)[idx];
907         mpw->wqe->mpw.eseg.mss = htons(length);
908         mpw->wqe->mpw.eseg.inline_hdr_sz = 0;
909         mpw->wqe->mpw.eseg.rsvd0 = 0;
910         mpw->wqe->mpw.eseg.rsvd1 = 0;
911         mpw->wqe->mpw.eseg.rsvd2 = 0;
912         mpw->wqe->mpw.ctrl.data[0] = htonl((MLX5_OPC_MOD_MPW << 24) |
913                                            (txq->wqe_ci << 8) |
914                                            MLX5_OPCODE_LSO_MPW);
915         mpw->wqe->mpw.ctrl.data[2] = 0;
916         mpw->wqe->mpw.ctrl.data[3] = 0;
917         mpw->data.dseg[0] = &mpw->wqe->mpw.dseg[0];
918         mpw->data.dseg[1] = &mpw->wqe->mpw.dseg[1];
919         mpw->data.dseg[2] = &(*dseg)[0];
920         mpw->data.dseg[3] = &(*dseg)[1];
921         mpw->data.dseg[4] = &(*dseg)[2];
922 }
923
924 /**
925  * Close a MPW session.
926  *
927  * @param txq
928  *   Pointer to TX queue structure.
929  * @param mpw
930  *   Pointer to MPW session structure.
931  */
932 static inline void
933 mlx5_mpw_close(struct txq *txq, struct mlx5_mpw *mpw)
934 {
935         unsigned int num = mpw->pkts_n;
936
937         /*
938          * Store size in multiple of 16 bytes. Control and Ethernet segments
939          * count as 2.
940          */
941         mpw->wqe->mpw.ctrl.data[1] = htonl(txq->qp_num_8s | (2 + num));
942         mpw->state = MLX5_MPW_STATE_CLOSED;
943         if (num < 3)
944                 ++txq->wqe_ci;
945         else
946                 txq->wqe_ci += 2;
947         tx_prefetch_wqe(txq, txq->wqe_ci);
948         tx_prefetch_wqe(txq, txq->wqe_ci + 1);
949 }
950
951 /**
952  * DPDK callback for TX with MPW support.
953  *
954  * @param dpdk_txq
955  *   Generic pointer to TX queue structure.
956  * @param[in] pkts
957  *   Packets to transmit.
958  * @param pkts_n
959  *   Number of packets in array.
960  *
961  * @return
962  *   Number of packets successfully transmitted (<= pkts_n).
963  */
964 uint16_t
965 mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
966 {
967         struct txq *txq = (struct txq *)dpdk_txq;
968         uint16_t elts_head = txq->elts_head;
969         const unsigned int elts_n = txq->elts_n;
970         unsigned int i = 0;
971         unsigned int j = 0;
972         unsigned int max;
973         unsigned int comp;
974         struct mlx5_mpw mpw = {
975                 .state = MLX5_MPW_STATE_CLOSED,
976         };
977
978         if (unlikely(!pkts_n))
979                 return 0;
980         /* Prefetch first packet cacheline. */
981         tx_prefetch_cqe(txq, txq->cq_ci);
982         tx_prefetch_wqe(txq, txq->wqe_ci);
983         tx_prefetch_wqe(txq, txq->wqe_ci + 1);
984         /* Start processing. */
985         txq_complete(txq);
986         max = (elts_n - (elts_head - txq->elts_tail));
987         if (max > elts_n)
988                 max -= elts_n;
989         do {
990                 struct rte_mbuf *buf = *(pkts++);
991                 unsigned int elts_head_next;
992                 uint32_t length;
993                 unsigned int segs_n = buf->nb_segs;
994                 uint32_t cs_flags = 0;
995
996                 /*
997                  * Make sure there is enough room to store this packet and
998                  * that one ring entry remains unused.
999                  */
1000                 assert(segs_n);
1001                 if (max < segs_n + 1)
1002                         break;
1003                 /* Do not bother with large packets MPW cannot handle. */
1004                 if (segs_n > MLX5_MPW_DSEG_MAX)
1005                         break;
1006                 max -= segs_n;
1007                 --pkts_n;
1008                 /* Should we enable HW CKSUM offload */
1009                 if (buf->ol_flags &
1010                     (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM))
1011                         cs_flags = MLX5_ETH_WQE_L3_CSUM | MLX5_ETH_WQE_L4_CSUM;
1012                 /* Retrieve packet information. */
1013                 length = PKT_LEN(buf);
1014                 assert(length);
1015                 /* Start new session if packet differs. */
1016                 if ((mpw.state == MLX5_MPW_STATE_OPENED) &&
1017                     ((mpw.len != length) ||
1018                      (segs_n != 1) ||
1019                      (mpw.wqe->mpw.eseg.cs_flags != cs_flags)))
1020                         mlx5_mpw_close(txq, &mpw);
1021                 if (mpw.state == MLX5_MPW_STATE_CLOSED) {
1022                         mlx5_mpw_new(txq, &mpw, length);
1023                         mpw.wqe->mpw.eseg.cs_flags = cs_flags;
1024                 }
1025                 /* Multi-segment packets must be alone in their MPW. */
1026                 assert((segs_n == 1) || (mpw.pkts_n == 0));
1027 #if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
1028                 length = 0;
1029 #endif
1030                 do {
1031                         volatile struct mlx5_wqe_data_seg *dseg;
1032                         uintptr_t addr;
1033
1034                         elts_head_next = (elts_head + 1) & (elts_n - 1);
1035                         assert(buf);
1036                         (*txq->elts)[elts_head] = buf;
1037                         dseg = mpw.data.dseg[mpw.pkts_n];
1038                         addr = rte_pktmbuf_mtod(buf, uintptr_t);
1039                         *dseg = (struct mlx5_wqe_data_seg){
1040                                 .byte_count = htonl(DATA_LEN(buf)),
1041                                 .lkey = txq_mp2mr(txq, txq_mb2mp(buf)),
1042                                 .addr = htonll(addr),
1043                         };
1044                         elts_head = elts_head_next;
1045 #if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
1046                         length += DATA_LEN(buf);
1047 #endif
1048                         buf = buf->next;
1049                         ++mpw.pkts_n;
1050                         ++j;
1051                 } while (--segs_n);
1052                 assert(length == mpw.len);
1053                 if (mpw.pkts_n == MLX5_MPW_DSEG_MAX)
1054                         mlx5_mpw_close(txq, &mpw);
1055                 elts_head = elts_head_next;
1056 #ifdef MLX5_PMD_SOFT_COUNTERS
1057                 /* Increment sent bytes counter. */
1058                 txq->stats.obytes += length;
1059 #endif
1060                 ++i;
1061         } while (pkts_n);
1062         /* Take a shortcut if nothing must be sent. */
1063         if (unlikely(i == 0))
1064                 return 0;
1065         /* Check whether completion threshold has been reached. */
1066         /* "j" includes both packets and segments. */
1067         comp = txq->elts_comp + j;
1068         if (comp >= MLX5_TX_COMP_THRESH) {
1069                 volatile union mlx5_wqe *wqe = mpw.wqe;
1070
1071                 /* Request completion on last WQE. */
1072                 wqe->mpw.ctrl.data[2] = htonl(8);
1073                 /* Save elts_head in unused "immediate" field of WQE. */
1074                 wqe->mpw.ctrl.data[3] = elts_head;
1075                 txq->elts_comp = 0;
1076         } else {
1077                 txq->elts_comp = comp;
1078         }
1079 #ifdef MLX5_PMD_SOFT_COUNTERS
1080         /* Increment sent packets counter. */
1081         txq->stats.opackets += i;
1082 #endif
1083         /* Ring QP doorbell. */
1084         if (mpw.state == MLX5_MPW_STATE_OPENED)
1085                 mlx5_mpw_close(txq, &mpw);
1086         mlx5_tx_dbrec(txq);
1087         txq->elts_head = elts_head;
1088         return i;
1089 }
1090
1091 /**
1092  * Open a MPW inline session.
1093  *
1094  * @param txq
1095  *   Pointer to TX queue structure.
1096  * @param mpw
1097  *   Pointer to MPW session structure.
1098  * @param length
1099  *   Packet length.
1100  */
1101 static inline void
1102 mlx5_mpw_inline_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
1103 {
1104         uint16_t idx = txq->wqe_ci & (txq->wqe_n - 1);
1105
1106         mpw->state = MLX5_MPW_INL_STATE_OPENED;
1107         mpw->pkts_n = 0;
1108         mpw->len = length;
1109         mpw->total_len = 0;
1110         mpw->wqe = &(*txq->wqes)[idx];
1111         mpw->wqe->mpw_inl.ctrl.data[0] = htonl((MLX5_OPC_MOD_MPW << 24) |
1112                                                (txq->wqe_ci << 8) |
1113                                                MLX5_OPCODE_LSO_MPW);
1114         mpw->wqe->mpw_inl.ctrl.data[2] = 0;
1115         mpw->wqe->mpw_inl.ctrl.data[3] = 0;
1116         mpw->wqe->mpw_inl.eseg.mss = htons(length);
1117         mpw->wqe->mpw_inl.eseg.inline_hdr_sz = 0;
1118         mpw->wqe->mpw_inl.eseg.cs_flags = 0;
1119         mpw->wqe->mpw_inl.eseg.rsvd0 = 0;
1120         mpw->wqe->mpw_inl.eseg.rsvd1 = 0;
1121         mpw->wqe->mpw_inl.eseg.rsvd2 = 0;
1122         mpw->data.raw = &mpw->wqe->mpw_inl.data[0];
1123 }
1124
1125 /**
1126  * Close a MPW inline session.
1127  *
1128  * @param txq
1129  *   Pointer to TX queue structure.
1130  * @param mpw
1131  *   Pointer to MPW session structure.
1132  */
1133 static inline void
1134 mlx5_mpw_inline_close(struct txq *txq, struct mlx5_mpw *mpw)
1135 {
1136         unsigned int size;
1137
1138         size = sizeof(*mpw->wqe) - MLX5_MWQE64_INL_DATA + mpw->total_len;
1139         /*
1140          * Store size in multiple of 16 bytes. Control and Ethernet segments
1141          * count as 2.
1142          */
1143         mpw->wqe->mpw_inl.ctrl.data[1] =
1144                 htonl(txq->qp_num_8s | ((size + 15) / 16));
1145         mpw->state = MLX5_MPW_STATE_CLOSED;
1146         mpw->wqe->mpw_inl.byte_cnt = htonl(mpw->total_len | MLX5_INLINE_SEG);
1147         txq->wqe_ci += (size + (sizeof(*mpw->wqe) - 1)) / sizeof(*mpw->wqe);
1148 }
1149
1150 /**
1151  * DPDK callback for TX with MPW inline support.
1152  *
1153  * @param dpdk_txq
1154  *   Generic pointer to TX queue structure.
1155  * @param[in] pkts
1156  *   Packets to transmit.
1157  * @param pkts_n
1158  *   Number of packets in array.
1159  *
1160  * @return
1161  *   Number of packets successfully transmitted (<= pkts_n).
1162  */
1163 uint16_t
1164 mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts,
1165                          uint16_t pkts_n)
1166 {
1167         struct txq *txq = (struct txq *)dpdk_txq;
1168         uint16_t elts_head = txq->elts_head;
1169         const unsigned int elts_n = txq->elts_n;
1170         unsigned int i = 0;
1171         unsigned int j = 0;
1172         unsigned int max;
1173         unsigned int comp;
1174         unsigned int inline_room = txq->max_inline;
1175         struct mlx5_mpw mpw = {
1176                 .state = MLX5_MPW_STATE_CLOSED,
1177         };
1178
1179         if (unlikely(!pkts_n))
1180                 return 0;
1181         /* Prefetch first packet cacheline. */
1182         tx_prefetch_cqe(txq, txq->cq_ci);
1183         tx_prefetch_wqe(txq, txq->wqe_ci);
1184         tx_prefetch_wqe(txq, txq->wqe_ci + 1);
1185         /* Start processing. */
1186         txq_complete(txq);
1187         max = (elts_n - (elts_head - txq->elts_tail));
1188         if (max > elts_n)
1189                 max -= elts_n;
1190         do {
1191                 struct rte_mbuf *buf = *(pkts++);
1192                 unsigned int elts_head_next;
1193                 uintptr_t addr;
1194                 uint32_t length;
1195                 unsigned int segs_n = buf->nb_segs;
1196                 uint32_t cs_flags = 0;
1197
1198                 /*
1199                  * Make sure there is enough room to store this packet and
1200                  * that one ring entry remains unused.
1201                  */
1202                 assert(segs_n);
1203                 if (max < segs_n + 1)
1204                         break;
1205                 /* Do not bother with large packets MPW cannot handle. */
1206                 if (segs_n > MLX5_MPW_DSEG_MAX)
1207                         break;
1208                 max -= segs_n;
1209                 --pkts_n;
1210                 /* Should we enable HW CKSUM offload */
1211                 if (buf->ol_flags &
1212                     (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM))
1213                         cs_flags = MLX5_ETH_WQE_L3_CSUM | MLX5_ETH_WQE_L4_CSUM;
1214                 /* Retrieve packet information. */
1215                 length = PKT_LEN(buf);
1216                 /* Start new session if packet differs. */
1217                 if (mpw.state == MLX5_MPW_STATE_OPENED) {
1218                         if ((mpw.len != length) ||
1219                             (segs_n != 1) ||
1220                             (mpw.wqe->mpw.eseg.cs_flags != cs_flags))
1221                                 mlx5_mpw_close(txq, &mpw);
1222                 } else if (mpw.state == MLX5_MPW_INL_STATE_OPENED) {
1223                         if ((mpw.len != length) ||
1224                             (segs_n != 1) ||
1225                             (length > inline_room) ||
1226                             (mpw.wqe->mpw_inl.eseg.cs_flags != cs_flags)) {
1227                                 mlx5_mpw_inline_close(txq, &mpw);
1228                                 inline_room = txq->max_inline;
1229                         }
1230                 }
1231                 if (mpw.state == MLX5_MPW_STATE_CLOSED) {
1232                         if ((segs_n != 1) ||
1233                             (length > inline_room)) {
1234                                 mlx5_mpw_new(txq, &mpw, length);
1235                                 mpw.wqe->mpw.eseg.cs_flags = cs_flags;
1236                         } else {
1237                                 mlx5_mpw_inline_new(txq, &mpw, length);
1238                                 mpw.wqe->mpw_inl.eseg.cs_flags = cs_flags;
1239                         }
1240                 }
1241                 /* Multi-segment packets must be alone in their MPW. */
1242                 assert((segs_n == 1) || (mpw.pkts_n == 0));
1243                 if (mpw.state == MLX5_MPW_STATE_OPENED) {
1244                         assert(inline_room == txq->max_inline);
1245 #if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
1246                         length = 0;
1247 #endif
1248                         do {
1249                                 volatile struct mlx5_wqe_data_seg *dseg;
1250
1251                                 elts_head_next =
1252                                         (elts_head + 1) & (elts_n - 1);
1253                                 assert(buf);
1254                                 (*txq->elts)[elts_head] = buf;
1255                                 dseg = mpw.data.dseg[mpw.pkts_n];
1256                                 addr = rte_pktmbuf_mtod(buf, uintptr_t);
1257                                 *dseg = (struct mlx5_wqe_data_seg){
1258                                         .byte_count = htonl(DATA_LEN(buf)),
1259                                         .lkey = txq_mp2mr(txq, txq_mb2mp(buf)),
1260                                         .addr = htonll(addr),
1261                                 };
1262                                 elts_head = elts_head_next;
1263 #if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
1264                                 length += DATA_LEN(buf);
1265 #endif
1266                                 buf = buf->next;
1267                                 ++mpw.pkts_n;
1268                                 ++j;
1269                         } while (--segs_n);
1270                         assert(length == mpw.len);
1271                         if (mpw.pkts_n == MLX5_MPW_DSEG_MAX)
1272                                 mlx5_mpw_close(txq, &mpw);
1273                 } else {
1274                         unsigned int max;
1275
1276                         assert(mpw.state == MLX5_MPW_INL_STATE_OPENED);
1277                         assert(length <= inline_room);
1278                         assert(length == DATA_LEN(buf));
1279                         elts_head_next = (elts_head + 1) & (elts_n - 1);
1280                         addr = rte_pktmbuf_mtod(buf, uintptr_t);
1281                         (*txq->elts)[elts_head] = buf;
1282                         /* Maximum number of bytes before wrapping. */
1283                         max = ((uintptr_t)&(*txq->wqes)[txq->wqe_n] -
1284                                (uintptr_t)mpw.data.raw);
1285                         if (length > max) {
1286                                 rte_memcpy((void *)(uintptr_t)mpw.data.raw,
1287                                            (void *)addr,
1288                                            max);
1289                                 mpw.data.raw =
1290                                         (volatile void *)&(*txq->wqes)[0];
1291                                 rte_memcpy((void *)(uintptr_t)mpw.data.raw,
1292                                            (void *)(addr + max),
1293                                            length - max);
1294                                 mpw.data.raw += length - max;
1295                         } else {
1296                                 rte_memcpy((void *)(uintptr_t)mpw.data.raw,
1297                                            (void *)addr,
1298                                            length);
1299                                 mpw.data.raw += length;
1300                         }
1301                         if ((uintptr_t)mpw.data.raw ==
1302                             (uintptr_t)&(*txq->wqes)[txq->wqe_n])
1303                                 mpw.data.raw =
1304                                         (volatile void *)&(*txq->wqes)[0];
1305                         ++mpw.pkts_n;
1306                         ++j;
1307                         if (mpw.pkts_n == MLX5_MPW_DSEG_MAX) {
1308                                 mlx5_mpw_inline_close(txq, &mpw);
1309                                 inline_room = txq->max_inline;
1310                         } else {
1311                                 inline_room -= length;
1312                         }
1313                 }
1314                 mpw.total_len += length;
1315                 elts_head = elts_head_next;
1316 #ifdef MLX5_PMD_SOFT_COUNTERS
1317                 /* Increment sent bytes counter. */
1318                 txq->stats.obytes += length;
1319 #endif
1320                 ++i;
1321         } while (pkts_n);
1322         /* Take a shortcut if nothing must be sent. */
1323         if (unlikely(i == 0))
1324                 return 0;
1325         /* Check whether completion threshold has been reached. */
1326         /* "j" includes both packets and segments. */
1327         comp = txq->elts_comp + j;
1328         if (comp >= MLX5_TX_COMP_THRESH) {
1329                 volatile union mlx5_wqe *wqe = mpw.wqe;
1330
1331                 /* Request completion on last WQE. */
1332                 wqe->mpw_inl.ctrl.data[2] = htonl(8);
1333                 /* Save elts_head in unused "immediate" field of WQE. */
1334                 wqe->mpw_inl.ctrl.data[3] = elts_head;
1335                 txq->elts_comp = 0;
1336         } else {
1337                 txq->elts_comp = comp;
1338         }
1339 #ifdef MLX5_PMD_SOFT_COUNTERS
1340         /* Increment sent packets counter. */
1341         txq->stats.opackets += i;
1342 #endif
1343         /* Ring QP doorbell. */
1344         if (mpw.state == MLX5_MPW_INL_STATE_OPENED)
1345                 mlx5_mpw_inline_close(txq, &mpw);
1346         else if (mpw.state == MLX5_MPW_STATE_OPENED)
1347                 mlx5_mpw_close(txq, &mpw);
1348         mlx5_tx_dbrec(txq);
1349         txq->elts_head = elts_head;
1350         return i;
1351 }
1352
1353 /**
1354  * Translate RX completion flags to packet type.
1355  *
1356  * @param[in] cqe
1357  *   Pointer to CQE.
1358  *
1359  * @note: fix mlx5_dev_supported_ptypes_get() if any change here.
1360  *
1361  * @return
1362  *   Packet type for struct rte_mbuf.
1363  */
1364 static inline uint32_t
1365 rxq_cq_to_pkt_type(volatile struct mlx5_cqe64 *cqe)
1366 {
1367         uint32_t pkt_type;
1368         uint8_t flags = cqe->l4_hdr_type_etc;
1369         uint8_t info = cqe->rsvd0[0];
1370
1371         if (info & IBV_EXP_CQ_RX_TUNNEL_PACKET)
1372                 pkt_type =
1373                         TRANSPOSE(flags,
1374                                   IBV_EXP_CQ_RX_OUTER_IPV4_PACKET,
1375                                   RTE_PTYPE_L3_IPV4) |
1376                         TRANSPOSE(flags,
1377                                   IBV_EXP_CQ_RX_OUTER_IPV6_PACKET,
1378                                   RTE_PTYPE_L3_IPV6) |
1379                         TRANSPOSE(flags,
1380                                   IBV_EXP_CQ_RX_IPV4_PACKET,
1381                                   RTE_PTYPE_INNER_L3_IPV4) |
1382                         TRANSPOSE(flags,
1383                                   IBV_EXP_CQ_RX_IPV6_PACKET,
1384                                   RTE_PTYPE_INNER_L3_IPV6);
1385         else
1386                 pkt_type =
1387                         TRANSPOSE(flags,
1388                                   MLX5_CQE_L3_HDR_TYPE_IPV6,
1389                                   RTE_PTYPE_L3_IPV6) |
1390                         TRANSPOSE(flags,
1391                                   MLX5_CQE_L3_HDR_TYPE_IPV4,
1392                                   RTE_PTYPE_L3_IPV4);
1393         return pkt_type;
1394 }
1395
1396 /**
1397  * Get size of the next packet for a given CQE. For compressed CQEs, the
1398  * consumer index is updated only once all packets of the current one have
1399  * been processed.
1400  *
1401  * @param rxq
1402  *   Pointer to RX queue.
1403  * @param cqe
1404  *   CQE to process.
1405  *
1406  * @return
1407  *   Packet size in bytes (0 if there is none), -1 in case of completion
1408  *   with error.
1409  */
1410 static inline int
1411 mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe64 *cqe,
1412                  uint16_t cqe_cnt)
1413 {
1414         struct rxq_zip *zip = &rxq->zip;
1415         uint16_t cqe_n = cqe_cnt + 1;
1416         int len = 0;
1417
1418         /* Process compressed data in the CQE and mini arrays. */
1419         if (zip->ai) {
1420                 volatile struct mlx5_mini_cqe8 (*mc)[8] =
1421                         (volatile struct mlx5_mini_cqe8 (*)[8])
1422                         (uintptr_t)(&(*rxq->cqes)[zip->ca & cqe_cnt].cqe64);
1423
1424                 len = ntohl((*mc)[zip->ai & 7].byte_cnt);
1425                 if ((++zip->ai & 7) == 0) {
1426                         /*
1427                          * Increment consumer index to skip the number of
1428                          * CQEs consumed. Hardware leaves holes in the CQ
1429                          * ring for software use.
1430                          */
1431                         zip->ca = zip->na;
1432                         zip->na += 8;
1433                 }
1434                 if (unlikely(rxq->zip.ai == rxq->zip.cqe_cnt)) {
1435                         uint16_t idx = rxq->cq_ci;
1436                         uint16_t end = zip->cq_ci;
1437
1438                         while (idx != end) {
1439                                 (*rxq->cqes)[idx & cqe_cnt].cqe64.op_own =
1440                                         MLX5_CQE_INVALIDATE;
1441                                 ++idx;
1442                         }
1443                         rxq->cq_ci = zip->cq_ci;
1444                         zip->ai = 0;
1445                 }
1446         /* No compressed data, get next CQE and verify if it is compressed. */
1447         } else {
1448                 int ret;
1449                 int8_t op_own;
1450
1451                 ret = check_cqe64(cqe, cqe_n, rxq->cq_ci);
1452                 if (unlikely(ret == 1))
1453                         return 0;
1454                 ++rxq->cq_ci;
1455                 op_own = cqe->op_own;
1456                 if (MLX5_CQE_FORMAT(op_own) == MLX5_COMPRESSED) {
1457                         volatile struct mlx5_mini_cqe8 (*mc)[8] =
1458                                 (volatile struct mlx5_mini_cqe8 (*)[8])
1459                                 (uintptr_t)(&(*rxq->cqes)[rxq->cq_ci &
1460                                                           cqe_cnt].cqe64);
1461
1462                         /* Fix endianness. */
1463                         zip->cqe_cnt = ntohl(cqe->byte_cnt);
1464                         /*
1465                          * Current mini array position is the one returned by
1466                          * check_cqe64().
1467                          *
1468                          * If completion comprises several mini arrays, as a
1469                          * special case the second one is located 7 CQEs after
1470                          * the initial CQE instead of 8 for subsequent ones.
1471                          */
1472                         zip->ca = rxq->cq_ci & cqe_cnt;
1473                         zip->na = zip->ca + 7;
1474                         /* Compute the next non compressed CQE. */
1475                         --rxq->cq_ci;
1476                         zip->cq_ci = rxq->cq_ci + zip->cqe_cnt;
1477                         /* Get packet size to return. */
1478                         len = ntohl((*mc)[0].byte_cnt);
1479                         zip->ai = 1;
1480                 } else {
1481                         len = ntohl(cqe->byte_cnt);
1482                 }
1483                 /* Error while receiving packet. */
1484                 if (unlikely(MLX5_CQE_OPCODE(op_own) == MLX5_CQE_RESP_ERR))
1485                         return -1;
1486         }
1487         return len;
1488 }
1489
1490 /**
1491  * Translate RX completion flags to offload flags.
1492  *
1493  * @param[in] rxq
1494  *   Pointer to RX queue structure.
1495  * @param[in] cqe
1496  *   Pointer to CQE.
1497  *
1498  * @return
1499  *   Offload flags (ol_flags) for struct rte_mbuf.
1500  */
1501 static inline uint32_t
1502 rxq_cq_to_ol_flags(struct rxq *rxq, volatile struct mlx5_cqe64 *cqe)
1503 {
1504         uint32_t ol_flags = 0;
1505         uint8_t l3_hdr = (cqe->l4_hdr_type_etc) & MLX5_CQE_L3_HDR_TYPE_MASK;
1506         uint8_t l4_hdr = (cqe->l4_hdr_type_etc) & MLX5_CQE_L4_HDR_TYPE_MASK;
1507         uint8_t info = cqe->rsvd0[0];
1508
1509         if ((l3_hdr == MLX5_CQE_L3_HDR_TYPE_IPV4) ||
1510             (l3_hdr == MLX5_CQE_L3_HDR_TYPE_IPV6))
1511                 ol_flags |=
1512                         (!(cqe->hds_ip_ext & MLX5_CQE_L3_OK) *
1513                          PKT_RX_IP_CKSUM_BAD);
1514         if ((l4_hdr == MLX5_CQE_L4_HDR_TYPE_TCP) ||
1515             (l4_hdr == MLX5_CQE_L4_HDR_TYPE_TCP_EMP_ACK) ||
1516             (l4_hdr == MLX5_CQE_L4_HDR_TYPE_TCP_ACK) ||
1517             (l4_hdr == MLX5_CQE_L4_HDR_TYPE_UDP))
1518                 ol_flags |=
1519                         (!(cqe->hds_ip_ext & MLX5_CQE_L4_OK) *
1520                          PKT_RX_L4_CKSUM_BAD);
1521         /*
1522          * PKT_RX_IP_CKSUM_BAD and PKT_RX_L4_CKSUM_BAD are used in place
1523          * of PKT_RX_EIP_CKSUM_BAD because the latter is not functional
1524          * (its value is 0).
1525          */
1526         if ((info & IBV_EXP_CQ_RX_TUNNEL_PACKET) && (rxq->csum_l2tun))
1527                 ol_flags |=
1528                         TRANSPOSE(~cqe->l4_hdr_type_etc,
1529                                   IBV_EXP_CQ_RX_OUTER_IP_CSUM_OK,
1530                                   PKT_RX_IP_CKSUM_BAD) |
1531                         TRANSPOSE(~cqe->l4_hdr_type_etc,
1532                                   IBV_EXP_CQ_RX_OUTER_TCP_UDP_CSUM_OK,
1533                                   PKT_RX_L4_CKSUM_BAD);
1534         return ol_flags;
1535 }
1536
1537 /**
1538  * DPDK callback for RX.
1539  *
1540  * @param dpdk_rxq
1541  *   Generic pointer to RX queue structure.
1542  * @param[out] pkts
1543  *   Array to store received packets.
1544  * @param pkts_n
1545  *   Maximum number of packets in array.
1546  *
1547  * @return
1548  *   Number of packets successfully received (<= pkts_n).
1549  */
1550 uint16_t
1551 mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
1552 {
1553         struct rxq *rxq = dpdk_rxq;
1554         const unsigned int wqe_cnt = rxq->elts_n - 1;
1555         const unsigned int cqe_cnt = rxq->cqe_n - 1;
1556         const unsigned int sges_n = rxq->sges_n;
1557         struct rte_mbuf *pkt = NULL;
1558         struct rte_mbuf *seg = NULL;
1559         volatile struct mlx5_cqe64 *cqe =
1560                 &(*rxq->cqes)[rxq->cq_ci & cqe_cnt].cqe64;
1561         unsigned int i = 0;
1562         unsigned int rq_ci = rxq->rq_ci << sges_n;
1563         int len;
1564
1565         while (pkts_n) {
1566                 unsigned int idx = rq_ci & wqe_cnt;
1567                 volatile struct mlx5_wqe_data_seg *wqe = &(*rxq->wqes)[idx];
1568                 struct rte_mbuf *rep = (*rxq->elts)[idx];
1569
1570                 if (pkt)
1571                         NEXT(seg) = rep;
1572                 seg = rep;
1573                 rte_prefetch0(seg);
1574                 rte_prefetch0(cqe);
1575                 rte_prefetch0(wqe);
1576                 rep = rte_mbuf_raw_alloc(rxq->mp);
1577                 if (unlikely(rep == NULL)) {
1578                         ++rxq->stats.rx_nombuf;
1579                         if (!pkt) {
1580                                 /*
1581                                  * no buffers before we even started,
1582                                  * bail out silently.
1583                                  */
1584                                 break;
1585                         }
1586                         while (pkt != seg) {
1587                                 assert(pkt != (*rxq->elts)[idx]);
1588                                 seg = NEXT(pkt);
1589                                 rte_mbuf_refcnt_set(pkt, 0);
1590                                 __rte_mbuf_raw_free(pkt);
1591                                 pkt = seg;
1592                         }
1593                         break;
1594                 }
1595                 if (!pkt) {
1596                         cqe = &(*rxq->cqes)[rxq->cq_ci & cqe_cnt].cqe64;
1597                         len = mlx5_rx_poll_len(rxq, cqe, cqe_cnt);
1598                         if (len == 0) {
1599                                 rte_mbuf_refcnt_set(rep, 0);
1600                                 __rte_mbuf_raw_free(rep);
1601                                 break;
1602                         }
1603                         if (unlikely(len == -1)) {
1604                                 /* RX error, packet is likely too large. */
1605                                 rte_mbuf_refcnt_set(rep, 0);
1606                                 __rte_mbuf_raw_free(rep);
1607                                 ++rxq->stats.idropped;
1608                                 goto skip;
1609                         }
1610                         pkt = seg;
1611                         assert(len >= (rxq->crc_present << 2));
1612                         /* Update packet information. */
1613                         pkt->packet_type = 0;
1614                         pkt->ol_flags = 0;
1615                         if (rxq->csum | rxq->csum_l2tun | rxq->vlan_strip |
1616                             rxq->crc_present) {
1617                                 if (rxq->csum) {
1618                                         pkt->packet_type =
1619                                                 rxq_cq_to_pkt_type(cqe);
1620                                         pkt->ol_flags =
1621                                                 rxq_cq_to_ol_flags(rxq, cqe);
1622                                 }
1623                                 if (cqe->l4_hdr_type_etc &
1624                                     MLX5_CQE_VLAN_STRIPPED) {
1625                                         pkt->ol_flags |= PKT_RX_VLAN_PKT |
1626                                                 PKT_RX_VLAN_STRIPPED;
1627                                         pkt->vlan_tci = ntohs(cqe->vlan_info);
1628                                 }
1629                                 if (rxq->crc_present)
1630                                         len -= ETHER_CRC_LEN;
1631                         }
1632                         PKT_LEN(pkt) = len;
1633                 }
1634                 DATA_LEN(rep) = DATA_LEN(seg);
1635                 PKT_LEN(rep) = PKT_LEN(seg);
1636                 SET_DATA_OFF(rep, DATA_OFF(seg));
1637                 NB_SEGS(rep) = NB_SEGS(seg);
1638                 PORT(rep) = PORT(seg);
1639                 NEXT(rep) = NULL;
1640                 (*rxq->elts)[idx] = rep;
1641                 /*
1642                  * Fill NIC descriptor with the new buffer.  The lkey and size
1643                  * of the buffers are already known, only the buffer address
1644                  * changes.
1645                  */
1646                 wqe->addr = htonll(rte_pktmbuf_mtod(rep, uintptr_t));
1647                 if (len > DATA_LEN(seg)) {
1648                         len -= DATA_LEN(seg);
1649                         ++NB_SEGS(pkt);
1650                         ++rq_ci;
1651                         continue;
1652                 }
1653                 DATA_LEN(seg) = len;
1654 #ifdef MLX5_PMD_SOFT_COUNTERS
1655                 /* Increment bytes counter. */
1656                 rxq->stats.ibytes += PKT_LEN(pkt);
1657 #endif
1658                 /* Return packet. */
1659                 *(pkts++) = pkt;
1660                 pkt = NULL;
1661                 --pkts_n;
1662                 ++i;
1663 skip:
1664                 /* Align consumer index to the next stride. */
1665                 rq_ci >>= sges_n;
1666                 ++rq_ci;
1667                 rq_ci <<= sges_n;
1668         }
1669         if (unlikely((i == 0) && ((rq_ci >> sges_n) == rxq->rq_ci)))
1670                 return 0;
1671         /* Update the consumer index. */
1672         rxq->rq_ci = rq_ci >> sges_n;
1673         rte_wmb();
1674         *rxq->cq_db = htonl(rxq->cq_ci);
1675         rte_wmb();
1676         *rxq->rq_db = htonl(rxq->rq_ci);
1677 #ifdef MLX5_PMD_SOFT_COUNTERS
1678         /* Increment packets counter. */
1679         rxq->stats.ipackets += i;
1680 #endif
1681         return i;
1682 }
1683
1684 /**
1685  * Dummy DPDK callback for TX.
1686  *
1687  * This function is used to temporarily replace the real callback during
1688  * unsafe control operations on the queue, or in case of error.
1689  *
1690  * @param dpdk_txq
1691  *   Generic pointer to TX queue structure.
1692  * @param[in] pkts
1693  *   Packets to transmit.
1694  * @param pkts_n
1695  *   Number of packets in array.
1696  *
1697  * @return
1698  *   Number of packets successfully transmitted (<= pkts_n).
1699  */
1700 uint16_t
1701 removed_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
1702 {
1703         (void)dpdk_txq;
1704         (void)pkts;
1705         (void)pkts_n;
1706         return 0;
1707 }
1708
1709 /**
1710  * Dummy DPDK callback for RX.
1711  *
1712  * This function is used to temporarily replace the real callback during
1713  * unsafe control operations on the queue, or in case of error.
1714  *
1715  * @param dpdk_rxq
1716  *   Generic pointer to RX queue structure.
1717  * @param[out] pkts
1718  *   Array to store received packets.
1719  * @param pkts_n
1720  *   Maximum number of packets in array.
1721  *
1722  * @return
1723  *   Number of packets successfully received (<= pkts_n).
1724  */
1725 uint16_t
1726 removed_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
1727 {
1728         (void)dpdk_rxq;
1729         (void)pkts;
1730         (void)pkts_n;
1731         return 0;
1732 }