78b6dd5a7e448f5be1062d3d0b17e081575a412f
[dpdk.git] / drivers / net / mlx4 / mlx4_rxtx.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2017 6WIND S.A.
3  * Copyright 2017 Mellanox Technologies, Ltd
4  */
5
6 /**
7  * @file
8  * Data plane functions for mlx4 driver.
9  */
10
11 #include <assert.h>
12 #include <stdint.h>
13 #include <string.h>
14
15 /* Verbs headers do not support -pedantic. */
16 #ifdef PEDANTIC
17 #pragma GCC diagnostic ignored "-Wpedantic"
18 #endif
19 #include <infiniband/verbs.h>
20 #ifdef PEDANTIC
21 #pragma GCC diagnostic error "-Wpedantic"
22 #endif
23
24 #include <rte_branch_prediction.h>
25 #include <rte_common.h>
26 #include <rte_io.h>
27 #include <rte_mbuf.h>
28 #include <rte_mempool.h>
29 #include <rte_prefetch.h>
30
31 #include "mlx4.h"
32 #include "mlx4_prm.h"
33 #include "mlx4_rxtx.h"
34 #include "mlx4_utils.h"
35
36 /**
37  * Pointer-value pair structure used in tx_post_send for saving the first
38  * DWORD (32 byte) of a TXBB.
39  */
40 struct pv {
41         volatile struct mlx4_wqe_data_seg *dseg;
42         uint32_t val;
43 };
44
45 /** A table to translate Rx completion flags to packet type. */
46 uint32_t mlx4_ptype_table[0x100] __rte_cache_aligned = {
47         /*
48          * The index to the array should have:
49          *  bit[7] - MLX4_CQE_L2_TUNNEL
50          *  bit[6] - MLX4_CQE_L2_TUNNEL_IPV4
51          *  bit[5] - MLX4_CQE_STATUS_UDP
52          *  bit[4] - MLX4_CQE_STATUS_TCP
53          *  bit[3] - MLX4_CQE_STATUS_IPV4OPT
54          *  bit[2] - MLX4_CQE_STATUS_IPV6
55          *  bit[1] - MLX4_CQE_STATUS_IPF
56          *  bit[0] - MLX4_CQE_STATUS_IPV4
57          * giving a total of up to 256 entries.
58          */
59         /* L2 */
60         [0x00] = RTE_PTYPE_L2_ETHER,
61         /* L3 */
62         [0x01] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
63                      RTE_PTYPE_L4_NONFRAG,
64         [0x02] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
65                      RTE_PTYPE_L4_FRAG,
66         [0x03] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
67                      RTE_PTYPE_L4_FRAG,
68         [0x04] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
69                      RTE_PTYPE_L4_NONFRAG,
70         [0x06] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
71                      RTE_PTYPE_L4_FRAG,
72         [0x08] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT |
73                      RTE_PTYPE_L4_NONFRAG,
74         [0x09] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT |
75                      RTE_PTYPE_L4_NONFRAG,
76         [0x0a] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT |
77                      RTE_PTYPE_L4_FRAG,
78         [0x0b] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT |
79                      RTE_PTYPE_L4_FRAG,
80         /* TCP */
81         [0x11] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
82                      RTE_PTYPE_L4_TCP,
83         [0x14] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
84                      RTE_PTYPE_L4_TCP,
85         [0x16] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
86                      RTE_PTYPE_L4_FRAG,
87         [0x18] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT |
88                      RTE_PTYPE_L4_TCP,
89         [0x19] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT |
90                      RTE_PTYPE_L4_TCP,
91         /* UDP */
92         [0x21] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
93                      RTE_PTYPE_L4_UDP,
94         [0x24] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
95                      RTE_PTYPE_L4_UDP,
96         [0x26] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
97                      RTE_PTYPE_L4_FRAG,
98         [0x28] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT |
99                      RTE_PTYPE_L4_UDP,
100         [0x29] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT |
101                      RTE_PTYPE_L4_UDP,
102         /* Tunneled - L3 IPV6 */
103         [0x80] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN,
104         [0x81] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
105                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
106                      RTE_PTYPE_INNER_L4_NONFRAG,
107         [0x82] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
108                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
109                      RTE_PTYPE_INNER_L4_FRAG,
110         [0x83] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
111                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
112                      RTE_PTYPE_INNER_L4_FRAG,
113         [0x84] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
114                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
115                      RTE_PTYPE_INNER_L4_NONFRAG,
116         [0x86] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
117                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
118                      RTE_PTYPE_INNER_L4_FRAG,
119         [0x88] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
120                      RTE_PTYPE_INNER_L3_IPV4_EXT |
121                      RTE_PTYPE_INNER_L4_NONFRAG,
122         [0x89] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
123                      RTE_PTYPE_INNER_L3_IPV4_EXT |
124                      RTE_PTYPE_INNER_L4_NONFRAG,
125         [0x8a] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
126                      RTE_PTYPE_INNER_L3_IPV4_EXT |
127                      RTE_PTYPE_INNER_L4_FRAG,
128         [0x8b] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
129                      RTE_PTYPE_INNER_L3_IPV4_EXT |
130                      RTE_PTYPE_INNER_L4_FRAG,
131         /* Tunneled - L3 IPV6, TCP */
132         [0x91] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
133                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
134                      RTE_PTYPE_INNER_L4_TCP,
135         [0x94] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
136                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
137                      RTE_PTYPE_INNER_L4_TCP,
138         [0x96] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
139                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
140                      RTE_PTYPE_INNER_L4_FRAG,
141         [0x98] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
142                      RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_TCP,
143         [0x99] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
144                      RTE_PTYPE_INNER_L3_IPV4_EXT | RTE_PTYPE_INNER_L4_TCP,
145         /* Tunneled - L3 IPV6, UDP */
146         [0xa1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
147                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
148                      RTE_PTYPE_INNER_L4_UDP,
149         [0xa4] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
150                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
151                      RTE_PTYPE_INNER_L4_UDP,
152         [0xa6] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
153                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
154                      RTE_PTYPE_INNER_L4_FRAG,
155         [0xa8] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
156                      RTE_PTYPE_INNER_L3_IPV4_EXT |
157                      RTE_PTYPE_INNER_L4_UDP,
158         [0xa9] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
159                      RTE_PTYPE_INNER_L3_IPV4_EXT |
160                      RTE_PTYPE_INNER_L4_UDP,
161         /* Tunneled - L3 IPV4 */
162         [0xc0] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN,
163         [0xc1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
164                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
165                      RTE_PTYPE_INNER_L4_NONFRAG,
166         [0xc2] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
167                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
168                      RTE_PTYPE_INNER_L4_FRAG,
169         [0xc3] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
170                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
171                      RTE_PTYPE_INNER_L4_FRAG,
172         [0xc4] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
173                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
174                      RTE_PTYPE_INNER_L4_NONFRAG,
175         [0xc6] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
176                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
177                      RTE_PTYPE_INNER_L4_FRAG,
178         [0xc8] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
179                      RTE_PTYPE_INNER_L3_IPV4_EXT |
180                      RTE_PTYPE_INNER_L4_NONFRAG,
181         [0xc9] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
182                      RTE_PTYPE_INNER_L3_IPV4_EXT |
183                      RTE_PTYPE_INNER_L4_NONFRAG,
184         [0xca] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
185                      RTE_PTYPE_INNER_L3_IPV4_EXT |
186                      RTE_PTYPE_INNER_L4_FRAG,
187         [0xcb] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
188                      RTE_PTYPE_INNER_L3_IPV4_EXT |
189                      RTE_PTYPE_INNER_L4_FRAG,
190         /* Tunneled - L3 IPV4, TCP */
191         [0xd1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
192                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
193                      RTE_PTYPE_INNER_L4_TCP,
194         [0xd4] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
195                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
196                      RTE_PTYPE_INNER_L4_TCP,
197         [0xd6] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
198                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
199                      RTE_PTYPE_INNER_L4_FRAG,
200         [0xd8] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
201                      RTE_PTYPE_INNER_L3_IPV4_EXT |
202                      RTE_PTYPE_INNER_L4_TCP,
203         [0xd9] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
204                      RTE_PTYPE_INNER_L3_IPV4_EXT |
205                      RTE_PTYPE_INNER_L4_TCP,
206         /* Tunneled - L3 IPV4, UDP */
207         [0xe1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
208                      RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
209                      RTE_PTYPE_INNER_L4_UDP,
210         [0xe4] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
211                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
212                      RTE_PTYPE_INNER_L4_UDP,
213         [0xe6] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
214                      RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
215                      RTE_PTYPE_INNER_L4_FRAG,
216         [0xe8] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
217                      RTE_PTYPE_INNER_L3_IPV4_EXT |
218                      RTE_PTYPE_INNER_L4_UDP,
219         [0xe9] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
220                      RTE_PTYPE_INNER_L3_IPV4_EXT |
221                      RTE_PTYPE_INNER_L4_UDP,
222 };
223
224 /**
225  * Stamp TXBB burst so it won't be reused by the HW.
226  *
227  * Routine is used when freeing WQE used by the chip or when failing
228  * building an WQ entry has failed leaving partial information on the queue.
229  *
230  * @param sq
231  *   Pointer to the SQ structure.
232  * @param start
233  *   Pointer to the first TXBB to stamp.
234  * @param end
235  *   Pointer to the followed end TXBB to stamp.
236  *
237  * @return
238  *   Stamping burst size in byte units.
239  */
240 static uint32_t
241 mlx4_txq_stamp_freed_wqe(struct mlx4_sq *sq, volatile uint32_t *start,
242                          volatile uint32_t *end)
243 {
244         uint32_t stamp = sq->stamp;
245         int32_t size = (intptr_t)end - (intptr_t)start;
246
247         assert(start != end);
248         /* Hold SQ ring wrap around. */
249         if (size < 0) {
250                 size = (int32_t)sq->size + size;
251                 do {
252                         *start = stamp;
253                         start += MLX4_SQ_STAMP_DWORDS;
254                 } while (start != (volatile uint32_t *)sq->eob);
255                 start = (volatile uint32_t *)sq->buf;
256                 /* Flip invalid stamping ownership. */
257                 stamp ^= RTE_BE32(1u << MLX4_SQ_OWNER_BIT);
258                 sq->stamp = stamp;
259                 if (start == end)
260                         return size;
261         }
262         do {
263                 *start = stamp;
264                 start += MLX4_SQ_STAMP_DWORDS;
265         } while (start != end);
266         return (uint32_t)size;
267 }
268
269 /**
270  * Manage Tx completions.
271  *
272  * When sending a burst, mlx4_tx_burst() posts several WRs.
273  * To improve performance, a completion event is only required once every
274  * MLX4_PMD_TX_PER_COMP_REQ sends. Doing so discards completion information
275  * for other WRs, but this information would not be used anyway.
276  *
277  * @param txq
278  *   Pointer to Tx queue structure.
279  * @param elts_m
280  *   Tx elements number mask.
281  * @param sq
282  *   Pointer to the SQ structure.
283  */
284 static void
285 mlx4_txq_complete(struct txq *txq, const unsigned int elts_m,
286                   struct mlx4_sq *sq)
287 {
288         unsigned int elts_tail = txq->elts_tail;
289         struct mlx4_cq *cq = &txq->mcq;
290         volatile struct mlx4_cqe *cqe;
291         uint32_t completed;
292         uint32_t cons_index = cq->cons_index;
293         volatile uint32_t *first_txbb;
294
295         /*
296          * Traverse over all CQ entries reported and handle each WQ entry
297          * reported by them.
298          */
299         do {
300                 cqe = (volatile struct mlx4_cqe *)mlx4_get_cqe(cq, cons_index);
301                 if (unlikely(!!(cqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK) ^
302                     !!(cons_index & cq->cqe_cnt)))
303                         break;
304 #ifndef NDEBUG
305                 /*
306                  * Make sure we read the CQE after we read the ownership bit.
307                  */
308                 rte_io_rmb();
309                 if (unlikely((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) ==
310                              MLX4_CQE_OPCODE_ERROR)) {
311                         volatile struct mlx4_err_cqe *cqe_err =
312                                 (volatile struct mlx4_err_cqe *)cqe;
313                         ERROR("%p CQE error - vendor syndrome: 0x%x"
314                               " syndrome: 0x%x\n",
315                               (void *)txq, cqe_err->vendor_err,
316                               cqe_err->syndrome);
317                         break;
318                 }
319 #endif /* NDEBUG */
320                 cons_index++;
321         } while (1);
322         completed = (cons_index - cq->cons_index) * txq->elts_comp_cd_init;
323         if (unlikely(!completed))
324                 return;
325         /* First stamping address is the end of the last one. */
326         first_txbb = (&(*txq->elts)[elts_tail & elts_m])->eocb;
327         elts_tail += completed;
328         /* The new tail element holds the end address. */
329         sq->remain_size += mlx4_txq_stamp_freed_wqe(sq, first_txbb,
330                 (&(*txq->elts)[elts_tail & elts_m])->eocb);
331         /* Update CQ consumer index. */
332         cq->cons_index = cons_index;
333         *cq->set_ci_db = rte_cpu_to_be_32(cons_index & MLX4_CQ_DB_CI_MASK);
334         txq->elts_tail = elts_tail;
335 }
336
337 /**
338  * Write Tx data segment to the SQ.
339  *
340  * @param dseg
341  *   Pointer to data segment in SQ.
342  * @param lkey
343  *   Memory region lkey.
344  * @param addr
345  *   Data address.
346  * @param byte_count
347  *   Big endian bytes count of the data to send.
348  */
349 static inline void
350 mlx4_fill_tx_data_seg(volatile struct mlx4_wqe_data_seg *dseg,
351                        uint32_t lkey, uintptr_t addr, rte_be32_t  byte_count)
352 {
353         dseg->addr = rte_cpu_to_be_64(addr);
354         dseg->lkey = lkey;
355 #if RTE_CACHE_LINE_SIZE < 64
356         /*
357          * Need a barrier here before writing the byte_count
358          * fields to make sure that all the data is visible
359          * before the byte_count field is set.
360          * Otherwise, if the segment begins a new cacheline,
361          * the HCA prefetcher could grab the 64-byte chunk and
362          * get a valid (!= 0xffffffff) byte count but stale
363          * data, and end up sending the wrong data.
364          */
365         rte_io_wmb();
366 #endif /* RTE_CACHE_LINE_SIZE */
367         dseg->byte_count = byte_count;
368 }
369
370 /**
371  * Write data segments of multi-segment packet.
372  *
373  * @param buf
374  *   Pointer to the first packet mbuf.
375  * @param txq
376  *   Pointer to Tx queue structure.
377  * @param ctrl
378  *   Pointer to the WQE control segment.
379  *
380  * @return
381  *   Pointer to the next WQE control segment on success, NULL otherwise.
382  */
383 static volatile struct mlx4_wqe_ctrl_seg *
384 mlx4_tx_burst_segs(struct rte_mbuf *buf, struct txq *txq,
385                    volatile struct mlx4_wqe_ctrl_seg *ctrl)
386 {
387         struct pv *pv = (struct pv *)txq->bounce_buf;
388         struct mlx4_sq *sq = &txq->msq;
389         struct rte_mbuf *sbuf = buf;
390         uint32_t lkey;
391         int pv_counter = 0;
392         int nb_segs = buf->nb_segs;
393         uint32_t wqe_size;
394         volatile struct mlx4_wqe_data_seg *dseg =
395                 (volatile struct mlx4_wqe_data_seg *)(ctrl + 1);
396
397         ctrl->fence_size = 1 + nb_segs;
398         wqe_size = RTE_ALIGN((uint32_t)(ctrl->fence_size << MLX4_SEG_SHIFT),
399                              MLX4_TXBB_SIZE);
400         /* Validate WQE size and WQE space in the send queue. */
401         if (sq->remain_size < wqe_size ||
402             wqe_size > MLX4_MAX_WQE_SIZE)
403                 return NULL;
404         /*
405          * Fill the data segments with buffer information.
406          * First WQE TXBB head segment is always control segment,
407          * so jump to tail TXBB data segments code for the first
408          * WQE data segments filling.
409          */
410         goto txbb_tail_segs;
411 txbb_head_seg:
412         /* Memory region key (big endian) for this memory pool. */
413         lkey = mlx4_tx_mb2mr(txq, sbuf);
414         if (unlikely(lkey == (uint32_t)-1)) {
415                 DEBUG("%p: unable to get MP <-> MR association",
416                       (void *)txq);
417                 return NULL;
418         }
419         /* Handle WQE wraparound. */
420         if (dseg >=
421                 (volatile struct mlx4_wqe_data_seg *)sq->eob)
422                 dseg = (volatile struct mlx4_wqe_data_seg *)
423                         sq->buf;
424         dseg->addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(sbuf, uintptr_t));
425         dseg->lkey = lkey;
426         /*
427          * This data segment starts at the beginning of a new
428          * TXBB, so we need to postpone its byte_count writing
429          * for later.
430          */
431         pv[pv_counter].dseg = dseg;
432         /*
433          * Zero length segment is treated as inline segment
434          * with zero data.
435          */
436         pv[pv_counter++].val = rte_cpu_to_be_32(sbuf->data_len ?
437                                                 sbuf->data_len : 0x80000000);
438         sbuf = sbuf->next;
439         dseg++;
440         nb_segs--;
441 txbb_tail_segs:
442         /* Jump to default if there are more than two segments remaining. */
443         switch (nb_segs) {
444         default:
445                 lkey = mlx4_tx_mb2mr(txq, sbuf);
446                 if (unlikely(lkey == (uint32_t)-1)) {
447                         DEBUG("%p: unable to get MP <-> MR association",
448                               (void *)txq);
449                         return NULL;
450                 }
451                 mlx4_fill_tx_data_seg(dseg, lkey,
452                                       rte_pktmbuf_mtod(sbuf, uintptr_t),
453                                       rte_cpu_to_be_32(sbuf->data_len ?
454                                                        sbuf->data_len :
455                                                        0x80000000));
456                 sbuf = sbuf->next;
457                 dseg++;
458                 nb_segs--;
459                 /* fallthrough */
460         case 2:
461                 lkey = mlx4_tx_mb2mr(txq, sbuf);
462                 if (unlikely(lkey == (uint32_t)-1)) {
463                         DEBUG("%p: unable to get MP <-> MR association",
464                               (void *)txq);
465                         return NULL;
466                 }
467                 mlx4_fill_tx_data_seg(dseg, lkey,
468                                       rte_pktmbuf_mtod(sbuf, uintptr_t),
469                                       rte_cpu_to_be_32(sbuf->data_len ?
470                                                        sbuf->data_len :
471                                                        0x80000000));
472                 sbuf = sbuf->next;
473                 dseg++;
474                 nb_segs--;
475                 /* fallthrough */
476         case 1:
477                 lkey = mlx4_tx_mb2mr(txq, sbuf);
478                 if (unlikely(lkey == (uint32_t)-1)) {
479                         DEBUG("%p: unable to get MP <-> MR association",
480                               (void *)txq);
481                         return NULL;
482                 }
483                 mlx4_fill_tx_data_seg(dseg, lkey,
484                                       rte_pktmbuf_mtod(sbuf, uintptr_t),
485                                       rte_cpu_to_be_32(sbuf->data_len ?
486                                                        sbuf->data_len :
487                                                        0x80000000));
488                 nb_segs--;
489                 if (nb_segs) {
490                         sbuf = sbuf->next;
491                         dseg++;
492                         goto txbb_head_seg;
493                 }
494                 /* fallthrough */
495         case 0:
496                 break;
497         }
498         /* Write the first DWORD of each TXBB save earlier. */
499         if (pv_counter) {
500                 /* Need a barrier here before writing the byte_count. */
501                 rte_io_wmb();
502                 for (--pv_counter; pv_counter  >= 0; pv_counter--)
503                         pv[pv_counter].dseg->byte_count = pv[pv_counter].val;
504         }
505         sq->remain_size -= wqe_size;
506         /* Align next WQE address to the next TXBB. */
507         return (volatile struct mlx4_wqe_ctrl_seg *)
508                 ((volatile uint8_t *)ctrl + wqe_size);
509 }
510
511 /**
512  * DPDK callback for Tx.
513  *
514  * @param dpdk_txq
515  *   Generic pointer to Tx queue structure.
516  * @param[in] pkts
517  *   Packets to transmit.
518  * @param pkts_n
519  *   Number of packets in array.
520  *
521  * @return
522  *   Number of packets successfully transmitted (<= pkts_n).
523  */
524 uint16_t
525 mlx4_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
526 {
527         struct txq *txq = (struct txq *)dpdk_txq;
528         unsigned int elts_head = txq->elts_head;
529         const unsigned int elts_n = txq->elts_n;
530         const unsigned int elts_m = elts_n - 1;
531         unsigned int bytes_sent = 0;
532         unsigned int i;
533         unsigned int max = elts_head - txq->elts_tail;
534         struct mlx4_sq *sq = &txq->msq;
535         volatile struct mlx4_wqe_ctrl_seg *ctrl;
536         struct txq_elt *elt;
537
538         assert(txq->elts_comp_cd != 0);
539         if (likely(max >= txq->elts_comp_cd_init))
540                 mlx4_txq_complete(txq, elts_m, sq);
541         max = elts_n - max;
542         assert(max >= 1);
543         assert(max <= elts_n);
544         /* Always leave one free entry in the ring. */
545         --max;
546         if (max > pkts_n)
547                 max = pkts_n;
548         elt = &(*txq->elts)[elts_head & elts_m];
549         /* First Tx burst element saves the next WQE control segment. */
550         ctrl = elt->wqe;
551         for (i = 0; (i != max); ++i) {
552                 struct rte_mbuf *buf = pkts[i];
553                 struct txq_elt *elt_next = &(*txq->elts)[++elts_head & elts_m];
554                 uint32_t owner_opcode = sq->owner_opcode;
555                 volatile struct mlx4_wqe_data_seg *dseg =
556                                 (volatile struct mlx4_wqe_data_seg *)(ctrl + 1);
557                 volatile struct mlx4_wqe_ctrl_seg *ctrl_next;
558                 union {
559                         uint32_t flags;
560                         uint16_t flags16[2];
561                 } srcrb;
562                 uint32_t lkey;
563
564                 /* Clean up old buffer. */
565                 if (likely(elt->buf != NULL)) {
566                         struct rte_mbuf *tmp = elt->buf;
567
568 #ifndef NDEBUG
569                         /* Poisoning. */
570                         memset(&elt->buf, 0x66, sizeof(struct rte_mbuf *));
571 #endif
572                         /* Faster than rte_pktmbuf_free(). */
573                         do {
574                                 struct rte_mbuf *next = tmp->next;
575
576                                 rte_pktmbuf_free_seg(tmp);
577                                 tmp = next;
578                         } while (tmp != NULL);
579                 }
580                 RTE_MBUF_PREFETCH_TO_FREE(elt_next->buf);
581                 if (buf->nb_segs == 1) {
582                         /* Validate WQE space in the send queue. */
583                         if (sq->remain_size < MLX4_TXBB_SIZE) {
584                                 elt->buf = NULL;
585                                 break;
586                         }
587                         lkey = mlx4_tx_mb2mr(txq, buf);
588                         if (unlikely(lkey == (uint32_t)-1)) {
589                                 /* MR does not exist. */
590                                 DEBUG("%p: unable to get MP <-> MR association",
591                                       (void *)txq);
592                                 elt->buf = NULL;
593                                 break;
594                         }
595                         mlx4_fill_tx_data_seg(dseg++, lkey,
596                                               rte_pktmbuf_mtod(buf, uintptr_t),
597                                               rte_cpu_to_be_32(buf->data_len));
598                         /* Set WQE size in 16-byte units. */
599                         ctrl->fence_size = 0x2;
600                         sq->remain_size -= MLX4_TXBB_SIZE;
601                         /* Align next WQE address to the next TXBB. */
602                         ctrl_next = ctrl + 0x4;
603                 } else {
604                         ctrl_next = mlx4_tx_burst_segs(buf, txq, ctrl);
605                         if (!ctrl_next) {
606                                 elt->buf = NULL;
607                                 break;
608                         }
609                 }
610                 /* Hold SQ ring wrap around. */
611                 if ((volatile uint8_t *)ctrl_next >= sq->eob) {
612                         ctrl_next = (volatile struct mlx4_wqe_ctrl_seg *)
613                                 ((volatile uint8_t *)ctrl_next - sq->size);
614                         /* Flip HW valid ownership. */
615                         sq->owner_opcode ^= 1u << MLX4_SQ_OWNER_BIT;
616                 }
617                 /*
618                  * For raw Ethernet, the SOLICIT flag is used to indicate
619                  * that no ICRC should be calculated.
620                  */
621                 if (--txq->elts_comp_cd == 0) {
622                         /* Save the completion burst end address. */
623                         elt_next->eocb = (volatile uint32_t *)ctrl_next;
624                         txq->elts_comp_cd = txq->elts_comp_cd_init;
625                         srcrb.flags = RTE_BE32(MLX4_WQE_CTRL_SOLICIT |
626                                                MLX4_WQE_CTRL_CQ_UPDATE);
627                 } else {
628                         srcrb.flags = RTE_BE32(MLX4_WQE_CTRL_SOLICIT);
629                 }
630                 /* Enable HW checksum offload if requested */
631                 if (txq->csum &&
632                     (buf->ol_flags &
633                      (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM))) {
634                         const uint64_t is_tunneled = (buf->ol_flags &
635                                                       (PKT_TX_TUNNEL_GRE |
636                                                        PKT_TX_TUNNEL_VXLAN));
637
638                         if (is_tunneled && txq->csum_l2tun) {
639                                 owner_opcode |= MLX4_WQE_CTRL_IIP_HDR_CSUM |
640                                                 MLX4_WQE_CTRL_IL4_HDR_CSUM;
641                                 if (buf->ol_flags & PKT_TX_OUTER_IP_CKSUM)
642                                         srcrb.flags |=
643                                             RTE_BE32(MLX4_WQE_CTRL_IP_HDR_CSUM);
644                         } else {
645                                 srcrb.flags |=
646                                         RTE_BE32(MLX4_WQE_CTRL_IP_HDR_CSUM |
647                                                 MLX4_WQE_CTRL_TCP_UDP_CSUM);
648                         }
649                 }
650                 if (txq->lb) {
651                         /*
652                          * Copy destination MAC address to the WQE, this allows
653                          * loopback in eSwitch, so that VFs and PF can
654                          * communicate with each other.
655                          */
656                         srcrb.flags16[0] = *(rte_pktmbuf_mtod(buf, uint16_t *));
657                         ctrl->imm = *(rte_pktmbuf_mtod_offset(buf, uint32_t *,
658                                               sizeof(uint16_t)));
659                 } else {
660                         ctrl->imm = 0;
661                 }
662                 ctrl->srcrb_flags = srcrb.flags;
663                 /*
664                  * Make sure descriptor is fully written before
665                  * setting ownership bit (because HW can start
666                  * executing as soon as we do).
667                  */
668                 rte_io_wmb();
669                 ctrl->owner_opcode = rte_cpu_to_be_32(owner_opcode);
670                 elt->buf = buf;
671                 bytes_sent += buf->pkt_len;
672                 ctrl = ctrl_next;
673                 elt = elt_next;
674         }
675         /* Take a shortcut if nothing must be sent. */
676         if (unlikely(i == 0))
677                 return 0;
678         /* Save WQE address of the next Tx burst element. */
679         elt->wqe = ctrl;
680         /* Increment send statistics counters. */
681         txq->stats.opackets += i;
682         txq->stats.obytes += bytes_sent;
683         /* Make sure that descriptors are written before doorbell record. */
684         rte_wmb();
685         /* Ring QP doorbell. */
686         rte_write32(txq->msq.doorbell_qpn, txq->msq.db);
687         txq->elts_head += i;
688         return i;
689 }
690
691 /**
692  * Translate Rx completion flags to packet type.
693  *
694  * @param[in] cqe
695  *   Pointer to CQE.
696  *
697  * @return
698  *   Packet type for struct rte_mbuf.
699  */
700 static inline uint32_t
701 rxq_cq_to_pkt_type(volatile struct mlx4_cqe *cqe,
702                    uint32_t l2tun_offload)
703 {
704         uint8_t idx = 0;
705         uint32_t pinfo = rte_be_to_cpu_32(cqe->vlan_my_qpn);
706         uint32_t status = rte_be_to_cpu_32(cqe->status);
707
708         /*
709          * The index to the array should have:
710          *  bit[7] - MLX4_CQE_L2_TUNNEL
711          *  bit[6] - MLX4_CQE_L2_TUNNEL_IPV4
712          */
713         if (l2tun_offload && (pinfo & MLX4_CQE_L2_TUNNEL))
714                 idx |= ((pinfo & MLX4_CQE_L2_TUNNEL) >> 20) |
715                        ((pinfo & MLX4_CQE_L2_TUNNEL_IPV4) >> 19);
716         /*
717          * The index to the array should have:
718          *  bit[5] - MLX4_CQE_STATUS_UDP
719          *  bit[4] - MLX4_CQE_STATUS_TCP
720          *  bit[3] - MLX4_CQE_STATUS_IPV4OPT
721          *  bit[2] - MLX4_CQE_STATUS_IPV6
722          *  bit[1] - MLX4_CQE_STATUS_IPF
723          *  bit[0] - MLX4_CQE_STATUS_IPV4
724          * giving a total of up to 256 entries.
725          */
726         idx |= ((status & MLX4_CQE_STATUS_PTYPE_MASK) >> 22);
727         if (status & MLX4_CQE_STATUS_IPV6)
728                 idx |= ((status & MLX4_CQE_STATUS_IPV6F) >> 11);
729         return mlx4_ptype_table[idx];
730 }
731
732 /**
733  * Translate Rx completion flags to offload flags.
734  *
735  * @param flags
736  *   Rx completion flags returned by mlx4_cqe_flags().
737  * @param csum
738  *   Whether Rx checksums are enabled.
739  * @param csum_l2tun
740  *   Whether Rx L2 tunnel checksums are enabled.
741  *
742  * @return
743  *   Offload flags (ol_flags) in mbuf format.
744  */
745 static inline uint32_t
746 rxq_cq_to_ol_flags(uint32_t flags, int csum, int csum_l2tun)
747 {
748         uint32_t ol_flags = 0;
749
750         if (csum)
751                 ol_flags |=
752                         mlx4_transpose(flags,
753                                        MLX4_CQE_STATUS_IP_HDR_CSUM_OK,
754                                        PKT_RX_IP_CKSUM_GOOD) |
755                         mlx4_transpose(flags,
756                                        MLX4_CQE_STATUS_TCP_UDP_CSUM_OK,
757                                        PKT_RX_L4_CKSUM_GOOD);
758         if ((flags & MLX4_CQE_L2_TUNNEL) && csum_l2tun)
759                 ol_flags |=
760                         mlx4_transpose(flags,
761                                        MLX4_CQE_L2_TUNNEL_IPOK,
762                                        PKT_RX_IP_CKSUM_GOOD) |
763                         mlx4_transpose(flags,
764                                        MLX4_CQE_L2_TUNNEL_L4_CSUM,
765                                        PKT_RX_L4_CKSUM_GOOD);
766         return ol_flags;
767 }
768
769 /**
770  * Extract checksum information from CQE flags.
771  *
772  * @param cqe
773  *   Pointer to CQE structure.
774  * @param csum
775  *   Whether Rx checksums are enabled.
776  * @param csum_l2tun
777  *   Whether Rx L2 tunnel checksums are enabled.
778  *
779  * @return
780  *   CQE checksum information.
781  */
782 static inline uint32_t
783 mlx4_cqe_flags(volatile struct mlx4_cqe *cqe, int csum, int csum_l2tun)
784 {
785         uint32_t flags = 0;
786
787         /*
788          * The relevant bits are in different locations on their
789          * CQE fields therefore we can join them in one 32bit
790          * variable.
791          */
792         if (csum)
793                 flags = (rte_be_to_cpu_32(cqe->status) &
794                          MLX4_CQE_STATUS_IPV4_CSUM_OK);
795         if (csum_l2tun)
796                 flags |= (rte_be_to_cpu_32(cqe->vlan_my_qpn) &
797                           (MLX4_CQE_L2_TUNNEL |
798                            MLX4_CQE_L2_TUNNEL_IPOK |
799                            MLX4_CQE_L2_TUNNEL_L4_CSUM |
800                            MLX4_CQE_L2_TUNNEL_IPV4));
801         return flags;
802 }
803
804 /**
805  * Poll one CQE from CQ.
806  *
807  * @param rxq
808  *   Pointer to the receive queue structure.
809  * @param[out] out
810  *   Just polled CQE.
811  *
812  * @return
813  *   Number of bytes of the CQE, 0 in case there is no completion.
814  */
815 static unsigned int
816 mlx4_cq_poll_one(struct rxq *rxq, volatile struct mlx4_cqe **out)
817 {
818         int ret = 0;
819         volatile struct mlx4_cqe *cqe = NULL;
820         struct mlx4_cq *cq = &rxq->mcq;
821
822         cqe = (volatile struct mlx4_cqe *)mlx4_get_cqe(cq, cq->cons_index);
823         if (!!(cqe->owner_sr_opcode & MLX4_CQE_OWNER_MASK) ^
824             !!(cq->cons_index & cq->cqe_cnt))
825                 goto out;
826         /*
827          * Make sure we read CQ entry contents after we've checked the
828          * ownership bit.
829          */
830         rte_rmb();
831         assert(!(cqe->owner_sr_opcode & MLX4_CQE_IS_SEND_MASK));
832         assert((cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) !=
833                MLX4_CQE_OPCODE_ERROR);
834         ret = rte_be_to_cpu_32(cqe->byte_cnt);
835         ++cq->cons_index;
836 out:
837         *out = cqe;
838         return ret;
839 }
840
841 /**
842  * DPDK callback for Rx with scattered packets support.
843  *
844  * @param dpdk_rxq
845  *   Generic pointer to Rx queue structure.
846  * @param[out] pkts
847  *   Array to store received packets.
848  * @param pkts_n
849  *   Maximum number of packets in array.
850  *
851  * @return
852  *   Number of packets successfully received (<= pkts_n).
853  */
854 uint16_t
855 mlx4_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
856 {
857         struct rxq *rxq = dpdk_rxq;
858         const uint32_t wr_cnt = (1 << rxq->elts_n) - 1;
859         const uint16_t sges_n = rxq->sges_n;
860         struct rte_mbuf *pkt = NULL;
861         struct rte_mbuf *seg = NULL;
862         unsigned int i = 0;
863         uint32_t rq_ci = rxq->rq_ci << sges_n;
864         int len = 0;
865
866         while (pkts_n) {
867                 volatile struct mlx4_cqe *cqe;
868                 uint32_t idx = rq_ci & wr_cnt;
869                 struct rte_mbuf *rep = (*rxq->elts)[idx];
870                 volatile struct mlx4_wqe_data_seg *scat = &(*rxq->wqes)[idx];
871
872                 /* Update the 'next' pointer of the previous segment. */
873                 if (pkt)
874                         seg->next = rep;
875                 seg = rep;
876                 rte_prefetch0(seg);
877                 rte_prefetch0(scat);
878                 rep = rte_mbuf_raw_alloc(rxq->mp);
879                 if (unlikely(rep == NULL)) {
880                         ++rxq->stats.rx_nombuf;
881                         if (!pkt) {
882                                 /*
883                                  * No buffers before we even started,
884                                  * bail out silently.
885                                  */
886                                 break;
887                         }
888                         while (pkt != seg) {
889                                 assert(pkt != (*rxq->elts)[idx]);
890                                 rep = pkt->next;
891                                 pkt->next = NULL;
892                                 pkt->nb_segs = 1;
893                                 rte_mbuf_raw_free(pkt);
894                                 pkt = rep;
895                         }
896                         break;
897                 }
898                 if (!pkt) {
899                         /* Looking for the new packet. */
900                         len = mlx4_cq_poll_one(rxq, &cqe);
901                         if (!len) {
902                                 rte_mbuf_raw_free(rep);
903                                 break;
904                         }
905                         if (unlikely(len < 0)) {
906                                 /* Rx error, packet is likely too large. */
907                                 rte_mbuf_raw_free(rep);
908                                 ++rxq->stats.idropped;
909                                 goto skip;
910                         }
911                         pkt = seg;
912                         assert(len >= (rxq->crc_present << 2));
913                         /* Update packet information. */
914                         pkt->packet_type =
915                                 rxq_cq_to_pkt_type(cqe, rxq->l2tun_offload);
916                         pkt->ol_flags = PKT_RX_RSS_HASH;
917                         pkt->hash.rss = cqe->immed_rss_invalid;
918                         if (rxq->crc_present)
919                                 len -= ETHER_CRC_LEN;
920                         pkt->pkt_len = len;
921                         if (rxq->csum | rxq->csum_l2tun) {
922                                 uint32_t flags =
923                                         mlx4_cqe_flags(cqe,
924                                                        rxq->csum,
925                                                        rxq->csum_l2tun);
926
927                                 pkt->ol_flags =
928                                         rxq_cq_to_ol_flags(flags,
929                                                            rxq->csum,
930                                                            rxq->csum_l2tun);
931                         }
932                 }
933                 rep->nb_segs = 1;
934                 rep->port = rxq->port_id;
935                 rep->data_len = seg->data_len;
936                 rep->data_off = seg->data_off;
937                 (*rxq->elts)[idx] = rep;
938                 /*
939                  * Fill NIC descriptor with the new buffer. The lkey and size
940                  * of the buffers are already known, only the buffer address
941                  * changes.
942                  */
943                 scat->addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(rep, uintptr_t));
944                 /* If there's only one MR, no need to replace LKey in WQE. */
945                 if (unlikely(mlx4_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
946                         scat->lkey = mlx4_rx_mb2mr(rxq, rep);
947                 if (len > seg->data_len) {
948                         len -= seg->data_len;
949                         ++pkt->nb_segs;
950                         ++rq_ci;
951                         continue;
952                 }
953                 /* The last segment. */
954                 seg->data_len = len;
955                 /* Increment bytes counter. */
956                 rxq->stats.ibytes += pkt->pkt_len;
957                 /* Return packet. */
958                 *(pkts++) = pkt;
959                 pkt = NULL;
960                 --pkts_n;
961                 ++i;
962 skip:
963                 /* Align consumer index to the next stride. */
964                 rq_ci >>= sges_n;
965                 ++rq_ci;
966                 rq_ci <<= sges_n;
967         }
968         if (unlikely(i == 0 && (rq_ci >> sges_n) == rxq->rq_ci))
969                 return 0;
970         /* Update the consumer index. */
971         rxq->rq_ci = rq_ci >> sges_n;
972         rte_wmb();
973         *rxq->rq_db = rte_cpu_to_be_32(rxq->rq_ci);
974         *rxq->mcq.set_ci_db =
975                 rte_cpu_to_be_32(rxq->mcq.cons_index & MLX4_CQ_DB_CI_MASK);
976         /* Increment packets counter. */
977         rxq->stats.ipackets += i;
978         return i;
979 }
980
981 /**
982  * Dummy DPDK callback for Tx.
983  *
984  * This function is used to temporarily replace the real callback during
985  * unsafe control operations on the queue, or in case of error.
986  *
987  * @param dpdk_txq
988  *   Generic pointer to Tx queue structure.
989  * @param[in] pkts
990  *   Packets to transmit.
991  * @param pkts_n
992  *   Number of packets in array.
993  *
994  * @return
995  *   Number of packets successfully transmitted (<= pkts_n).
996  */
997 uint16_t
998 mlx4_tx_burst_removed(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
999 {
1000         (void)dpdk_txq;
1001         (void)pkts;
1002         (void)pkts_n;
1003         return 0;
1004 }
1005
1006 /**
1007  * Dummy DPDK callback for Rx.
1008  *
1009  * This function is used to temporarily replace the real callback during
1010  * unsafe control operations on the queue, or in case of error.
1011  *
1012  * @param dpdk_rxq
1013  *   Generic pointer to Rx queue structure.
1014  * @param[out] pkts
1015  *   Array to store received packets.
1016  * @param pkts_n
1017  *   Maximum number of packets in array.
1018  *
1019  * @return
1020  *   Number of packets successfully received (<= pkts_n).
1021  */
1022 uint16_t
1023 mlx4_rx_burst_removed(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
1024 {
1025         (void)dpdk_rxq;
1026         (void)pkts;
1027         (void)pkts_n;
1028         return 0;
1029 }