]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mlx5/mlx5_en/mlx5_en_tx.c
MFC r341578 and r341655:
[FreeBSD/FreeBSD.git] / sys / dev / mlx5 / mlx5_en / mlx5_en_tx.c
1 /*-
2  * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27
28 #include "en.h"
29 #include <machine/atomic.h>
30
31 static inline bool
32 mlx5e_do_send_cqe(struct mlx5e_sq *sq)
33 {
34         sq->cev_counter++;
35         /* interleave the CQEs */
36         if (sq->cev_counter >= sq->cev_factor) {
37                 sq->cev_counter = 0;
38                 return (1);
39         }
40         return (0);
41 }
42
43 void
44 mlx5e_send_nop(struct mlx5e_sq *sq, u32 ds_cnt)
45 {
46         u16 pi = sq->pc & sq->wq.sz_m1;
47         struct mlx5e_tx_wqe *wqe = mlx5_wq_cyc_get_wqe(&sq->wq, pi);
48
49         memset(&wqe->ctrl, 0, sizeof(wqe->ctrl));
50
51         wqe->ctrl.opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | MLX5_OPCODE_NOP);
52         wqe->ctrl.qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt);
53         if (mlx5e_do_send_cqe(sq))
54                 wqe->ctrl.fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE;
55         else
56                 wqe->ctrl.fm_ce_se = 0;
57
58         /* Copy data for doorbell */
59         memcpy(sq->doorbell.d32, &wqe->ctrl, sizeof(sq->doorbell.d32));
60
61         sq->mbuf[pi].mbuf = NULL;
62         sq->mbuf[pi].num_bytes = 0;
63         sq->mbuf[pi].num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS);
64         sq->pc += sq->mbuf[pi].num_wqebbs;
65 }
66
67 #if (__FreeBSD_version >= 1100000)
68 static uint32_t mlx5e_hash_value;
69
70 static void
71 mlx5e_hash_init(void *arg)
72 {
73         mlx5e_hash_value = m_ether_tcpip_hash_init();
74 }
75
76 /* Make kernel call mlx5e_hash_init after the random stack finished initializing */
77 SYSINIT(mlx5e_hash_init, SI_SUB_RANDOM, SI_ORDER_ANY, &mlx5e_hash_init, NULL);
78 #endif
79
80 static struct mlx5e_sq *
81 mlx5e_select_queue(struct ifnet *ifp, struct mbuf *mb)
82 {
83         struct mlx5e_priv *priv = ifp->if_softc;
84         struct mlx5e_channel * volatile *ppch;
85         struct mlx5e_channel *pch;
86         u32 ch;
87         u32 tc;
88
89         ppch = priv->channel;
90
91         /* check if channels are successfully opened */
92         if (unlikely(ppch == NULL))
93                 return (NULL);
94
95         /* obtain VLAN information if present */
96         if (mb->m_flags & M_VLANTAG) {
97                 tc = (mb->m_pkthdr.ether_vtag >> 13);
98                 if (tc >= priv->num_tc)
99                         tc = priv->default_vlan_prio;
100         } else {
101                 tc = priv->default_vlan_prio;
102         }
103
104         ch = priv->params.num_channels;
105
106 #ifdef RATELIMIT
107         if (mb->m_pkthdr.snd_tag != NULL) {
108                 struct mlx5e_sq *sq;
109
110                 /* check for route change */
111                 if (mb->m_pkthdr.snd_tag->ifp != ifp)
112                         return (NULL);
113
114                 /* get pointer to sendqueue */
115                 sq = container_of(mb->m_pkthdr.snd_tag,
116                     struct mlx5e_rl_channel, m_snd_tag)->sq;
117
118                 /* check if valid */
119                 if (sq != NULL && sq->stopped == 0)
120                         return (sq);
121
122                 /* FALLTHROUGH */
123         }
124 #endif
125         /* check if flowid is set */
126         if (M_HASHTYPE_GET(mb) != M_HASHTYPE_NONE) {
127 #ifdef RSS
128                 u32 temp;
129
130                 if (rss_hash2bucket(mb->m_pkthdr.flowid,
131                     M_HASHTYPE_GET(mb), &temp) == 0)
132                         ch = temp % ch;
133                 else
134 #endif
135                         ch = (mb->m_pkthdr.flowid % 128) % ch;
136         } else {
137 #if (__FreeBSD_version >= 1100000)
138                 ch = m_ether_tcpip_hash(MBUF_HASHFLAG_L3 |
139                     MBUF_HASHFLAG_L4, mb, mlx5e_hash_value) % ch;
140 #else
141                 /*
142                  * m_ether_tcpip_hash not present in stable, so just
143                  * throw unhashed mbufs on queue 0
144                  */
145                 ch = 0;
146 #endif
147         }
148
149         /* check if channel is allocated and not stopped */
150         pch = ppch[ch];
151         if (likely(pch != NULL && pch->sq[tc].stopped == 0))
152                 return (&pch->sq[tc]);
153         return (NULL);
154 }
155
156 static inline u16
157 mlx5e_get_inline_hdr_size(struct mlx5e_sq *sq, struct mbuf *mb)
158 {
159
160         switch(sq->min_inline_mode) {
161         case MLX5_INLINE_MODE_NONE:
162                 /*
163                  * When inline mode is NONE, we do not need to copy
164                  * headers into WQEs, except when vlan tag framing is
165                  * requested. Hardware might offload vlan tagging on
166                  * transmit. This is a separate capability, which is
167                  * known to be disabled on ConnectX-5 due to a hardware
168                  * bug RM 931383. If vlan_inline_cap is not present and
169                  * the packet has vlan tag, fall back to inlining.
170                  */
171                 if ((mb->m_flags & M_VLANTAG) != 0 &&
172                     sq->vlan_inline_cap == 0)
173                         break;
174                 return (0);
175         case MLX5_INLINE_MODE_L2:
176                 /*
177                  * Due to hardware limitations, when trust mode is
178                  * DSCP, the hardware may request MLX5_INLINE_MODE_L2
179                  * while it really needs all L2 headers and the 4 first
180                  * bytes of the IP header (which include the
181                  * TOS/traffic-class).
182                  *
183                  * To avoid doing a firmware command for querying the
184                  * trust state and parsing the mbuf for doing
185                  * unnecessary checks (VLAN/eth_type) in the fast path,
186                  * we are going for the worth case (22 Bytes) if
187                  * the mb->m_pkthdr.len allows it.
188                  */
189                 if (mb->m_pkthdr.len > ETHER_HDR_LEN +
190                     ETHER_VLAN_ENCAP_LEN + 4)
191                         return (MIN(sq->max_inline, ETHER_HDR_LEN +
192                             ETHER_VLAN_ENCAP_LEN + 4));
193                 break;
194         }
195         return (MIN(sq->max_inline, mb->m_pkthdr.len));
196 }
197
198 static int
199 mlx5e_get_header_size(struct mbuf *mb)
200 {
201         struct ether_vlan_header *eh;
202         struct tcphdr *th;
203         struct ip *ip;
204         int ip_hlen, tcp_hlen;
205         struct ip6_hdr *ip6;
206         uint16_t eth_type;
207         int eth_hdr_len;
208
209         eh = mtod(mb, struct ether_vlan_header *);
210         if (mb->m_len < ETHER_HDR_LEN)
211                 return (0);
212         if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
213                 eth_type = ntohs(eh->evl_proto);
214                 eth_hdr_len = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
215         } else {
216                 eth_type = ntohs(eh->evl_encap_proto);
217                 eth_hdr_len = ETHER_HDR_LEN;
218         }
219         if (mb->m_len < eth_hdr_len)
220                 return (0);
221         switch (eth_type) {
222         case ETHERTYPE_IP:
223                 ip = (struct ip *)(mb->m_data + eth_hdr_len);
224                 if (mb->m_len < eth_hdr_len + sizeof(*ip))
225                         return (0);
226                 if (ip->ip_p != IPPROTO_TCP)
227                         return (0);
228                 ip_hlen = ip->ip_hl << 2;
229                 eth_hdr_len += ip_hlen;
230                 break;
231         case ETHERTYPE_IPV6:
232                 ip6 = (struct ip6_hdr *)(mb->m_data + eth_hdr_len);
233                 if (mb->m_len < eth_hdr_len + sizeof(*ip6))
234                         return (0);
235                 if (ip6->ip6_nxt != IPPROTO_TCP)
236                         return (0);
237                 eth_hdr_len += sizeof(*ip6);
238                 break;
239         default:
240                 return (0);
241         }
242         if (mb->m_len < eth_hdr_len + sizeof(*th))
243                 return (0);
244         th = (struct tcphdr *)(mb->m_data + eth_hdr_len);
245         tcp_hlen = th->th_off << 2;
246         eth_hdr_len += tcp_hlen;
247         if (mb->m_len < eth_hdr_len)
248                 return (0);
249         return (eth_hdr_len);
250 }
251
252 static int
253 mlx5e_sq_xmit(struct mlx5e_sq *sq, struct mbuf **mbp)
254 {
255         bus_dma_segment_t segs[MLX5E_MAX_TX_MBUF_FRAGS];
256         struct mlx5_wqe_data_seg *dseg;
257         struct mlx5e_tx_wqe *wqe;
258         struct ifnet *ifp;
259         int nsegs;
260         int err;
261         int x;
262         struct mbuf *mb = *mbp;
263         u16 ds_cnt;
264         u16 ihs;
265         u16 pi;
266         u8 opcode;
267
268         /* Return ENOBUFS if the queue is full */
269         if (unlikely(!mlx5e_sq_has_room_for(sq, 2 * MLX5_SEND_WQE_MAX_WQEBBS)))
270                 return (ENOBUFS);
271
272         /* Align SQ edge with NOPs to avoid WQE wrap around */
273         pi = ((~sq->pc) & sq->wq.sz_m1);
274         if (pi < (MLX5_SEND_WQE_MAX_WQEBBS - 1)) {
275                 /* Send one multi NOP message instead of many */
276                 mlx5e_send_nop(sq, (pi + 1) * MLX5_SEND_WQEBB_NUM_DS);
277                 pi = ((~sq->pc) & sq->wq.sz_m1);
278                 if (pi < (MLX5_SEND_WQE_MAX_WQEBBS - 1))
279                         return (ENOMEM);
280         }
281
282         /* Setup local variables */
283         pi = sq->pc & sq->wq.sz_m1;
284         wqe = mlx5_wq_cyc_get_wqe(&sq->wq, pi);
285         ifp = sq->ifp;
286
287         memset(wqe, 0, sizeof(*wqe));
288
289         /* Send a copy of the frame to the BPF listener, if any */
290         if (ifp != NULL && ifp->if_bpf != NULL)
291                 ETHER_BPF_MTAP(ifp, mb);
292
293         if (mb->m_pkthdr.csum_flags & (CSUM_IP | CSUM_TSO)) {
294                 wqe->eth.cs_flags |= MLX5_ETH_WQE_L3_CSUM;
295         }
296         if (mb->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP | CSUM_UDP_IPV6 | CSUM_TCP_IPV6 | CSUM_TSO)) {
297                 wqe->eth.cs_flags |= MLX5_ETH_WQE_L4_CSUM;
298         }
299         if (wqe->eth.cs_flags == 0) {
300                 sq->stats.csum_offload_none++;
301         }
302         if (mb->m_pkthdr.csum_flags & CSUM_TSO) {
303                 u32 payload_len;
304                 u32 mss = mb->m_pkthdr.tso_segsz;
305                 u32 num_pkts;
306
307                 wqe->eth.mss = cpu_to_be16(mss);
308                 opcode = MLX5_OPCODE_LSO;
309                 ihs = mlx5e_get_header_size(mb);
310                 payload_len = mb->m_pkthdr.len - ihs;
311                 if (payload_len == 0)
312                         num_pkts = 1;
313                 else
314                         num_pkts = DIV_ROUND_UP(payload_len, mss);
315                 sq->mbuf[pi].num_bytes = payload_len + (num_pkts * ihs);
316
317                 sq->stats.tso_packets++;
318                 sq->stats.tso_bytes += payload_len;
319         } else {
320                 opcode = MLX5_OPCODE_SEND;
321                 ihs = mlx5e_get_inline_hdr_size(sq, mb);
322                 sq->mbuf[pi].num_bytes = max_t (unsigned int,
323                     mb->m_pkthdr.len, ETHER_MIN_LEN - ETHER_CRC_LEN);
324         }
325         if (ihs == 0) {
326                 if ((mb->m_flags & M_VLANTAG) != 0) {
327                         wqe->eth.vlan_cmd = htons(0x8000); /* bit 0 CVLAN */
328                         wqe->eth.vlan_hdr = htons(mb->m_pkthdr.ether_vtag);
329                 } else {
330                         wqe->eth.inline_hdr_sz = 0;
331                 }
332         } else {
333                 if ((mb->m_flags & M_VLANTAG) != 0) {
334                         struct ether_vlan_header *eh = (struct ether_vlan_header
335                             *)wqe->eth.inline_hdr_start;
336
337                         /* Range checks */
338                         if (ihs > (MLX5E_MAX_TX_INLINE - ETHER_VLAN_ENCAP_LEN))
339                                 ihs = (MLX5E_MAX_TX_INLINE -
340                                     ETHER_VLAN_ENCAP_LEN);
341                         else if (ihs < ETHER_HDR_LEN) {
342                                 err = EINVAL;
343                                 goto tx_drop;
344                         }
345                         m_copydata(mb, 0, ETHER_HDR_LEN, (caddr_t)eh);
346                         m_adj(mb, ETHER_HDR_LEN);
347                         /* Insert 4 bytes VLAN tag into data stream */
348                         eh->evl_proto = eh->evl_encap_proto;
349                         eh->evl_encap_proto = htons(ETHERTYPE_VLAN);
350                         eh->evl_tag = htons(mb->m_pkthdr.ether_vtag);
351                         /* Copy rest of header data, if any */
352                         m_copydata(mb, 0, ihs - ETHER_HDR_LEN, (caddr_t)(eh +
353                             1));
354                         m_adj(mb, ihs - ETHER_HDR_LEN);
355                         /* Extend header by 4 bytes */
356                         ihs += ETHER_VLAN_ENCAP_LEN;
357                 } else {
358                         m_copydata(mb, 0, ihs, wqe->eth.inline_hdr_start);
359                         m_adj(mb, ihs);
360                 }
361                 wqe->eth.inline_hdr_sz = cpu_to_be16(ihs);
362         }
363
364         ds_cnt = sizeof(*wqe) / MLX5_SEND_WQE_DS;
365         if (ihs > sizeof(wqe->eth.inline_hdr_start)) {
366                 ds_cnt += DIV_ROUND_UP(ihs - sizeof(wqe->eth.inline_hdr_start),
367                     MLX5_SEND_WQE_DS);
368         }
369         dseg = ((struct mlx5_wqe_data_seg *)&wqe->ctrl) + ds_cnt;
370
371         err = bus_dmamap_load_mbuf_sg(sq->dma_tag, sq->mbuf[pi].dma_map,
372             mb, segs, &nsegs, BUS_DMA_NOWAIT);
373         if (err == EFBIG) {
374                 /* Update statistics */
375                 sq->stats.defragged++;
376                 /* Too many mbuf fragments */
377                 mb = m_defrag(*mbp, M_NOWAIT);
378                 if (mb == NULL) {
379                         mb = *mbp;
380                         goto tx_drop;
381                 }
382                 /* Try again */
383                 err = bus_dmamap_load_mbuf_sg(sq->dma_tag, sq->mbuf[pi].dma_map,
384                     mb, segs, &nsegs, BUS_DMA_NOWAIT);
385         }
386         /* Catch errors */
387         if (err != 0)
388                 goto tx_drop;
389
390         /* Make sure all mbuf data, if any, is written to RAM */
391         if (nsegs != 0) {
392                 bus_dmamap_sync(sq->dma_tag, sq->mbuf[pi].dma_map,
393                     BUS_DMASYNC_PREWRITE);
394         } else {
395                 /* All data was inlined, free the mbuf. */
396                 bus_dmamap_unload(sq->dma_tag, sq->mbuf[pi].dma_map);
397                 m_freem(mb);
398                 mb = NULL;
399         }
400
401         for (x = 0; x != nsegs; x++) {
402                 if (segs[x].ds_len == 0)
403                         continue;
404                 dseg->addr = cpu_to_be64((uint64_t)segs[x].ds_addr);
405                 dseg->lkey = sq->mkey_be;
406                 dseg->byte_count = cpu_to_be32((uint32_t)segs[x].ds_len);
407                 dseg++;
408         }
409
410         ds_cnt = (dseg - ((struct mlx5_wqe_data_seg *)&wqe->ctrl));
411
412         wqe->ctrl.opmod_idx_opcode = cpu_to_be32((sq->pc << 8) | opcode);
413         wqe->ctrl.qpn_ds = cpu_to_be32((sq->sqn << 8) | ds_cnt);
414         if (mlx5e_do_send_cqe(sq))
415                 wqe->ctrl.fm_ce_se = MLX5_WQE_CTRL_CQ_UPDATE;
416         else
417                 wqe->ctrl.fm_ce_se = 0;
418
419         /* Copy data for doorbell */
420         memcpy(sq->doorbell.d32, &wqe->ctrl, sizeof(sq->doorbell.d32));
421
422         /* Store pointer to mbuf */
423         sq->mbuf[pi].mbuf = mb;
424         sq->mbuf[pi].num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS);
425         sq->pc += sq->mbuf[pi].num_wqebbs;
426
427         sq->stats.packets++;
428         *mbp = NULL;    /* safety clear */
429         return (0);
430
431 tx_drop:
432         sq->stats.dropped++;
433         *mbp = NULL;
434         m_freem(mb);
435         return err;
436 }
437
438 static void
439 mlx5e_poll_tx_cq(struct mlx5e_sq *sq, int budget)
440 {
441         u16 sqcc;
442
443         /*
444          * sq->cc must be updated only after mlx5_cqwq_update_db_record(),
445          * otherwise a cq overrun may occur
446          */
447         sqcc = sq->cc;
448
449         while (budget > 0) {
450                 struct mlx5_cqe64 *cqe;
451                 struct mbuf *mb;
452                 u16 x;
453                 u16 ci;
454
455                 cqe = mlx5e_get_cqe(&sq->cq);
456                 if (!cqe)
457                         break;
458
459                 mlx5_cqwq_pop(&sq->cq.wq);
460
461                 /* update budget according to the event factor */
462                 budget -= sq->cev_factor;
463
464                 for (x = 0; x != sq->cev_factor; x++) {
465                         ci = sqcc & sq->wq.sz_m1;
466                         mb = sq->mbuf[ci].mbuf;
467                         sq->mbuf[ci].mbuf = NULL;       /* Safety clear */
468
469                         if (mb == NULL) {
470                                 if (sq->mbuf[ci].num_bytes == 0) {
471                                         /* NOP */
472                                         sq->stats.nop++;
473                                 }
474                         } else {
475                                 bus_dmamap_sync(sq->dma_tag, sq->mbuf[ci].dma_map,
476                                     BUS_DMASYNC_POSTWRITE);
477                                 bus_dmamap_unload(sq->dma_tag, sq->mbuf[ci].dma_map);
478
479                                 /* Free transmitted mbuf */
480                                 m_freem(mb);
481                         }
482                         sqcc += sq->mbuf[ci].num_wqebbs;
483                 }
484         }
485
486         mlx5_cqwq_update_db_record(&sq->cq.wq);
487
488         /* Ensure cq space is freed before enabling more cqes */
489         atomic_thread_fence_rel();
490
491         sq->cc = sqcc;
492 }
493
494 static int
495 mlx5e_xmit_locked(struct ifnet *ifp, struct mlx5e_sq *sq, struct mbuf *mb)
496 {
497         int err = 0;
498
499         if (unlikely((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 ||
500             sq->stopped != 0)) {
501                 m_freem(mb);
502                 return (ENETDOWN);
503         }
504
505         /* Do transmit */
506         if (mlx5e_sq_xmit(sq, &mb) != 0) {
507                 /* NOTE: m_freem() is NULL safe */
508                 m_freem(mb);
509                 err = ENOBUFS;
510         }
511
512         /* Check if we need to write the doorbell */
513         if (likely(sq->doorbell.d64 != 0)) {
514                 mlx5e_tx_notify_hw(sq, sq->doorbell.d32, 0);
515                 sq->doorbell.d64 = 0;
516         }
517
518         /*
519          * Check if we need to start the event timer which flushes the
520          * transmit ring on timeout:
521          */
522         if (unlikely(sq->cev_next_state == MLX5E_CEV_STATE_INITIAL &&
523             sq->cev_factor != 1)) {
524                 /* start the timer */
525                 mlx5e_sq_cev_timeout(sq);
526         } else {
527                 /* don't send NOPs yet */
528                 sq->cev_next_state = MLX5E_CEV_STATE_HOLD_NOPS;
529         }
530         return (err);
531 }
532
533 int
534 mlx5e_xmit(struct ifnet *ifp, struct mbuf *mb)
535 {
536         struct mlx5e_sq *sq;
537         int ret;
538
539         sq = mlx5e_select_queue(ifp, mb);
540         if (unlikely(sq == NULL)) {
541 #ifdef RATELIMIT
542                 /* Check for route change */
543                 if (mb->m_pkthdr.snd_tag != NULL &&
544                     mb->m_pkthdr.snd_tag->ifp != ifp) {
545                         /* Free mbuf */
546                         m_freem(mb);
547
548                         /*
549                          * Tell upper layers about route change and to
550                          * re-transmit this packet:
551                          */
552                         return (EAGAIN);
553                 }
554 #endif
555                 /* Free mbuf */
556                 m_freem(mb);
557
558                 /* Invalid send queue */
559                 return (ENXIO);
560         }
561
562         mtx_lock(&sq->lock);
563         ret = mlx5e_xmit_locked(ifp, sq, mb);
564         mtx_unlock(&sq->lock);
565
566         return (ret);
567 }
568
569 void
570 mlx5e_tx_cq_comp(struct mlx5_core_cq *mcq)
571 {
572         struct mlx5e_sq *sq = container_of(mcq, struct mlx5e_sq, cq.mcq);
573
574         mtx_lock(&sq->comp_lock);
575         mlx5e_poll_tx_cq(sq, MLX5E_BUDGET_MAX);
576         mlx5e_cq_arm(&sq->cq, MLX5_GET_DOORBELL_LOCK(&sq->priv->doorbell_lock));
577         mtx_unlock(&sq->comp_lock);
578 }