c6f6010cbb2e91703e765e504264da6b78067682
[samplevnf.git] / VNFs / DPPD-PROX / tx_pkt.c
1 /*
2 // Copyright (c) 2010-2017 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16
17 #include <rte_ethdev.h>
18 #include <rte_version.h>
19
20 #include "rx_pkt.h"
21 #include "tx_pkt.h"
22 #include "task_base.h"
23 #include "stats.h"
24 #include "prefetch.h"
25 #include "prox_assert.h"
26 #include "log.h"
27 #include "mbuf_utils.h"
28
29 static void buf_pkt_single(struct task_base *tbase, struct rte_mbuf *mbuf, const uint8_t out)
30 {
31         const uint16_t prod = tbase->ws_mbuf->idx[out].prod++;
32         tbase->ws_mbuf->mbuf[out][prod & WS_MBUF_MASK] = mbuf;
33 }
34
35 static inline void buf_pkt_all(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
36 {
37         for (uint16_t j = 0; j < n_pkts; ++j) {
38                 if (unlikely(out[j] >= OUT_HANDLED)) {
39                         rte_pktmbuf_free(mbufs[j]);
40                         if (out[j] == OUT_HANDLED)
41                                 TASK_STATS_ADD_DROP_HANDLED(&tbase->aux->stats, 1);
42                         else
43                                 TASK_STATS_ADD_DROP_DISCARD(&tbase->aux->stats, 1);
44                 }
45                 else {
46                         buf_pkt_single(tbase, mbufs[j], out[j]);
47                 }
48         }
49 }
50 #define MAX_PMD_TX 32
51
52 /* The following help functions also report stats. Therefore we need
53    to pass the task_base struct. */
54 static inline int txhw_drop(const struct port_queue *port_queue, struct rte_mbuf **mbufs, uint16_t n_pkts, __attribute__((unused)) struct task_base *tbase)
55 {
56         uint16_t ntx;
57         int ret;
58
59         /* TX vector mode can't transmit more than 32 packets */
60         if (n_pkts > MAX_PMD_TX) {
61                 ntx = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, MAX_PMD_TX);
62                 ntx += rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs + ntx, n_pkts - ntx);
63         } else {
64                 ntx = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, n_pkts);
65         }
66
67         TASK_STATS_ADD_TX(&tbase->aux->stats, ntx);
68         ret =  n_pkts - ntx;
69         if (ntx < n_pkts) {
70                 TASK_STATS_ADD_DROP_TX_FAIL(&tbase->aux->stats, n_pkts - ntx);
71                 if (tbase->tx_pkt == tx_pkt_bw) {
72                         uint32_t drop_bytes = 0;
73                         do {
74                                 drop_bytes += mbuf_wire_size(mbufs[ntx]);
75                                 rte_pktmbuf_free(mbufs[ntx++]);
76                         } while (ntx < n_pkts);
77                         TASK_STATS_ADD_DROP_BYTES(&tbase->aux->stats, drop_bytes);
78                 }
79                 else {
80                         do {
81                                 rte_pktmbuf_free(mbufs[ntx++]);
82                         } while (ntx < n_pkts);
83                 }
84         }
85         return ret;
86 }
87
88 static inline int txhw_no_drop(const struct port_queue *port_queue, struct rte_mbuf **mbufs, uint16_t n_pkts, __attribute__((unused)) struct task_base *tbase)
89 {
90         uint16_t ret;
91         uint16_t n = n_pkts;
92
93         TASK_STATS_ADD_TX(&tbase->aux->stats, n_pkts);
94
95         do {
96                 ret = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, n_pkts);
97                 mbufs += ret;
98                 n_pkts -= ret;
99         }
100         while (n_pkts);
101         return (n != ret);
102 }
103
104 static inline int ring_enq_drop(struct rte_ring *ring, struct rte_mbuf *const *mbufs, uint16_t n_pkts, __attribute__((unused)) struct task_base *tbase)
105 {
106         int ret = 0;
107         /* return 0 on succes, -ENOBUFS on failure */
108         // Rings can be single or multiproducer (ctrl rings are multi producer)
109 #if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1)
110         if (unlikely(rte_ring_enqueue_bulk(ring, (void *const *)mbufs, n_pkts))) {
111 #else
112         if (unlikely(rte_ring_enqueue_bulk(ring, (void *const *)mbufs, n_pkts, NULL) == 0)) {
113 #endif
114                 ret = n_pkts;
115                 if (tbase->tx_pkt == tx_pkt_bw) {
116                         uint32_t drop_bytes = 0;
117                         for (uint16_t i = 0; i < n_pkts; ++i) {
118                                 drop_bytes += mbuf_wire_size(mbufs[i]);
119                                 rte_pktmbuf_free(mbufs[i]);
120                         }
121                         TASK_STATS_ADD_DROP_BYTES(&tbase->aux->stats, drop_bytes);
122                         TASK_STATS_ADD_DROP_TX_FAIL(&tbase->aux->stats, n_pkts);
123                 }
124                 else {
125                         for (uint16_t i = 0; i < n_pkts; ++i)
126                                 rte_pktmbuf_free(mbufs[i]);
127                         TASK_STATS_ADD_DROP_TX_FAIL(&tbase->aux->stats, n_pkts);
128                 }
129         }
130         else {
131                 TASK_STATS_ADD_TX(&tbase->aux->stats, n_pkts);
132         }
133         return ret;
134 }
135
136 static inline int ring_enq_no_drop(struct rte_ring *ring, struct rte_mbuf *const *mbufs, uint16_t n_pkts, __attribute__((unused)) struct task_base *tbase)
137 {
138         int i = 0;
139 #if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1)
140         while (rte_ring_enqueue_bulk(ring, (void *const *)mbufs, n_pkts)) {
141 #else
142         while (rte_ring_enqueue_bulk(ring, (void *const *)mbufs, n_pkts, NULL) == 0) {
143 #endif
144                 i++;
145         };
146         TASK_STATS_ADD_TX(&tbase->aux->stats, n_pkts);
147         return (i != 0);
148 }
149
150 void flush_queues_hw(struct task_base *tbase)
151 {
152         uint16_t prod, cons;
153
154         for (uint8_t i = 0; i < tbase->tx_params_hw.nb_txports; ++i) {
155                 prod = tbase->ws_mbuf->idx[i].prod;
156                 cons = tbase->ws_mbuf->idx[i].cons;
157
158                 if (prod != cons) {
159                         tbase->ws_mbuf->idx[i].prod = 0;
160                         tbase->ws_mbuf->idx[i].cons = 0;
161                         txhw_drop(&tbase->tx_params_hw.tx_port_queue[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), prod - cons, tbase);
162                 }
163         }
164
165         tbase->flags &= ~FLAG_TX_FLUSH;
166 }
167
168 void flush_queues_sw(struct task_base *tbase)
169 {
170         uint16_t prod, cons;
171
172         for (uint8_t i = 0; i < tbase->tx_params_sw.nb_txrings; ++i) {
173                 prod = tbase->ws_mbuf->idx[i].prod;
174                 cons = tbase->ws_mbuf->idx[i].cons;
175
176                 if (prod != cons) {
177                         tbase->ws_mbuf->idx[i].prod = 0;
178                         tbase->ws_mbuf->idx[i].cons = 0;
179                         ring_enq_drop(tbase->tx_params_sw.tx_rings[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), prod - cons, tbase);
180                 }
181         }
182         tbase->flags &= ~FLAG_TX_FLUSH;
183 }
184
185 void flush_queues_no_drop_hw(struct task_base *tbase)
186 {
187         uint16_t prod, cons;
188
189         for (uint8_t i = 0; i < tbase->tx_params_hw.nb_txports; ++i) {
190                 prod = tbase->ws_mbuf->idx[i].prod;
191                 cons = tbase->ws_mbuf->idx[i].cons;
192
193                 if (prod != cons) {
194                         tbase->ws_mbuf->idx[i].prod = 0;
195                         tbase->ws_mbuf->idx[i].cons = 0;
196                         txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), prod - cons, tbase);
197                 }
198         }
199
200         tbase->flags &= ~FLAG_TX_FLUSH;
201 }
202
203 void flush_queues_no_drop_sw(struct task_base *tbase)
204 {
205         uint16_t prod, cons;
206
207         for (uint8_t i = 0; i < tbase->tx_params_sw.nb_txrings; ++i) {
208                 prod = tbase->ws_mbuf->idx[i].prod;
209                 cons = tbase->ws_mbuf->idx[i].cons;
210
211                 if (prod != cons) {
212                         tbase->ws_mbuf->idx[i].prod = 0;
213                         tbase->ws_mbuf->idx[i].cons = 0;
214                         ring_enq_no_drop(tbase->tx_params_sw.tx_rings[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), prod - cons, tbase);
215                 }
216         }
217         tbase->flags &= ~FLAG_TX_FLUSH;
218 }
219
220 /* "try" functions try to send packets to sw/hw w/o failing or blocking;
221    They return if ring/queue is full and are used by aggregators.
222    "try" functions do not have drop/no drop flavors
223    They are only implemented in never_discard mode (as by default they
224    use only one outgoing ring. */
225 uint16_t tx_try_self(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts)
226 {
227         if (n_pkts < 64) {
228                 tx_pkt_never_discard_self(tbase, mbufs, n_pkts, NULL);
229                 return n_pkts;
230         } else {
231                 tx_pkt_never_discard_self(tbase, mbufs, 64, NULL);
232                 return 64;
233         }
234 }
235
236 uint16_t tx_try_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts)
237 {
238         const int bulk_size = 64;
239         uint16_t ret = bulk_size, sent = 0, n_bulks;
240         n_bulks = n_pkts >> __builtin_ctz(bulk_size);
241
242         for (int i = 0; i < n_bulks; i++) {
243 #if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1)
244                 ret = rte_ring_enqueue_burst(tbase->tx_params_sw.tx_rings[0], (void *const *)mbufs, bulk_size);
245 #else
246                 ret = rte_ring_enqueue_burst(tbase->tx_params_sw.tx_rings[0], (void *const *)mbufs, bulk_size, NULL);
247 #endif
248                 mbufs += ret;
249                 sent += ret;
250                 if (ret != bulk_size)
251                         break;
252         }
253         if ((ret == bulk_size) && (n_pkts & (bulk_size - 1))) {
254 #if RTE_VERSION < RTE_VERSION_NUM(17,5,0,1)
255                 ret = rte_ring_enqueue_burst(tbase->tx_params_sw.tx_rings[0], (void *const *)mbufs, (n_pkts & (bulk_size - 1)));
256 #else
257                 ret = rte_ring_enqueue_burst(tbase->tx_params_sw.tx_rings[0], (void *const *)mbufs, (n_pkts & (bulk_size - 1)), NULL);
258 #endif
259                 mbufs += ret;
260                 sent += ret;
261         }
262         TASK_STATS_ADD_TX(&tbase->aux->stats, sent);
263         return sent;
264 }
265
266 uint16_t tx_try_hw1(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts)
267 {
268         const struct port_queue *port_queue = &tbase->tx_params_hw.tx_port_queue[0];
269         const int bulk_size = 64;
270         uint16_t ret = bulk_size, n_bulks, sent = 0;
271         n_bulks = n_pkts >>  __builtin_ctz(bulk_size);
272
273         for (int i = 0; i < n_bulks; i++) {
274                 ret = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, bulk_size);
275                 mbufs += ret;
276                 sent += ret;
277                 if (ret != bulk_size)
278                         break;
279         }
280         if ((ret == bulk_size) && (n_pkts & (bulk_size - 1))) {
281                 ret = rte_eth_tx_burst(port_queue->port, port_queue->queue, mbufs, (n_pkts & (bulk_size - 1)));
282                 mbufs += ret;
283                 sent += ret;
284         }
285         TASK_STATS_ADD_TX(&tbase->aux->stats, sent);
286         return sent;
287 }
288
289 int tx_pkt_no_drop_never_discard_hw1_lat_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out)
290 {
291         return txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_pkts, tbase);
292 }
293
294 int tx_pkt_no_drop_never_discard_hw1_thrpt_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out)
295 {
296         static uint8_t fake_out[MAX_PKT_BURST] = {0};
297         int ret = 0;
298         if (n_pkts == MAX_PKT_BURST) {
299                 // First xmit what was queued
300                 uint16_t prod, cons;
301
302                 prod = tbase->ws_mbuf->idx[0].prod;
303                 cons = tbase->ws_mbuf->idx[0].cons;
304
305                 if ((uint16_t)(prod - cons)){
306                         tbase->flags &= ~FLAG_TX_FLUSH;
307                         tbase->ws_mbuf->idx[0].prod = 0;
308                         tbase->ws_mbuf->idx[0].cons = 0;
309                         ret+= txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[0], tbase->ws_mbuf->mbuf[0] + (cons & WS_MBUF_MASK), (uint16_t)(prod - cons), tbase);
310                 }
311                 ret+= txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_pkts, tbase);
312         } else {
313                 ret+= tx_pkt_no_drop_hw(tbase, mbufs, n_pkts, fake_out);
314         }
315         return ret;
316 }
317
318 int tx_pkt_never_discard_hw1_lat_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out)
319 {
320         return txhw_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_pkts, tbase);
321 }
322
323 int tx_pkt_never_discard_hw1_thrpt_opt(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out)
324 {
325         static uint8_t fake_out[MAX_PKT_BURST] = {0};
326         int ret = 0;
327         if (n_pkts == MAX_PKT_BURST) {
328                 // First xmit what was queued
329                 uint16_t prod, cons;
330
331                 prod = tbase->ws_mbuf->idx[0].prod;
332                 cons = tbase->ws_mbuf->idx[0].cons;
333
334                 if ((uint16_t)(prod - cons)){
335                         tbase->flags &= ~FLAG_TX_FLUSH;
336                         tbase->ws_mbuf->idx[0].prod = 0;
337                         tbase->ws_mbuf->idx[0].cons = 0;
338                         ret+= txhw_drop(&tbase->tx_params_hw.tx_port_queue[0], tbase->ws_mbuf->mbuf[0] + (cons & WS_MBUF_MASK), (uint16_t)(prod - cons), tbase);
339                 }
340                 ret+= txhw_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_pkts, tbase);
341         } else {
342                 ret+= tx_pkt_hw(tbase, mbufs, n_pkts, fake_out);
343         }
344         return ret;
345 }
346
347 /* Transmit to hw using tx_params_hw_sw structure
348    This function is used  to transmit to hw when tx_params_hw_sw should be used
349    i.e. when the task needs to transmit both to hw and sw */
350 int tx_pkt_no_drop_never_discard_hw1_no_pointer(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out)
351 {
352         txhw_no_drop(&tbase->tx_params_hw_sw.tx_port_queue, mbufs, n_pkts, tbase);
353         return 0;
354 }
355
356 int tx_pkt_no_drop_never_discard_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out)
357 {
358         return ring_enq_no_drop(tbase->tx_params_sw.tx_rings[0], mbufs, n_pkts, tbase);
359 }
360
361 int tx_pkt_never_discard_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out)
362 {
363         return ring_enq_drop(tbase->tx_params_sw.tx_rings[0], mbufs, n_pkts, tbase);
364 }
365
366 static uint16_t tx_pkt_free_dropped(__attribute__((unused)) struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out)
367 {
368         uint64_t v = 0;
369         uint16_t i;
370         /* The most probable and most important optimize case is if
371            the no packets should be dropped. */
372         for (i = 0; i + 8 < n_pkts; i += 8) {
373                 v |= *((uint64_t*)(&out[i]));
374         }
375         for (; i < n_pkts; ++i) {
376                 v |= out[i];
377         }
378
379         if (unlikely(v)) {
380                 /* At least some packets need to be dropped, so the
381                    mbufs array needs to be updated. */
382                 uint16_t n_kept = 0;
383                 uint16_t n_discard = 0;
384                 for (uint16_t i = 0; i < n_pkts; ++i) {
385                         if (unlikely(out[i] >= OUT_HANDLED)) {
386                                 rte_pktmbuf_free(mbufs[i]);
387                                 n_discard += out[i] == OUT_DISCARD;
388                                 continue;
389                         }
390                         mbufs[n_kept++] = mbufs[i];
391                 }
392                 TASK_STATS_ADD_DROP_DISCARD(&tbase->aux->stats, n_discard);
393                 TASK_STATS_ADD_DROP_HANDLED(&tbase->aux->stats, n_pkts - n_kept - n_discard);
394                 return n_kept;
395         }
396         return n_pkts;
397 }
398
399 int tx_pkt_no_drop_hw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out)
400 {
401         const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out);
402         int ret = 0;
403
404         if (likely(n_kept))
405                 ret = txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_kept, tbase);
406         return ret;
407 }
408
409 int tx_pkt_no_drop_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out)
410 {
411         const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out);
412         int ret = 0;
413
414         if (likely(n_kept))
415                 ret = ring_enq_no_drop(tbase->tx_params_sw.tx_rings[0], mbufs, n_kept, tbase);
416         return ret;
417 }
418
419 int tx_pkt_hw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out)
420 {
421         const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out);
422
423         if (likely(n_kept))
424                 return txhw_drop(&tbase->tx_params_hw.tx_port_queue[0], mbufs, n_kept, tbase);
425         return n_pkts;
426 }
427
428 int tx_pkt_sw1(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out)
429 {
430         const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out);
431
432         if (likely(n_kept))
433                 return ring_enq_drop(tbase->tx_params_sw.tx_rings[0], mbufs, n_kept, tbase);
434         return 0;
435 }
436
437 int tx_pkt_self(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, uint8_t *out)
438 {
439         const uint16_t n_kept = tx_pkt_free_dropped(tbase, mbufs, n_pkts, out);
440
441         TASK_STATS_ADD_TX(&tbase->aux->stats, n_kept);
442         tbase->ws_mbuf->idx[0].nb_rx = n_kept;
443         struct rte_mbuf **tx_mbuf = tbase->ws_mbuf->mbuf[0] + (tbase->ws_mbuf->idx[0].prod & WS_MBUF_MASK);
444         for (uint16_t i = 0; i < n_kept; ++i) {
445                 tx_mbuf[i] = mbufs[i];
446         }
447         return 0;
448 }
449
450 int tx_pkt_never_discard_self(struct task_base *tbase, struct rte_mbuf **mbufs, const uint16_t n_pkts, __attribute__((unused)) uint8_t *out)
451 {
452         TASK_STATS_ADD_TX(&tbase->aux->stats, n_pkts);
453         tbase->ws_mbuf->idx[0].nb_rx = n_pkts;
454         struct rte_mbuf **tx_mbuf = tbase->ws_mbuf->mbuf[0] + (tbase->ws_mbuf->idx[0].prod & WS_MBUF_MASK);
455         for (uint16_t i = 0; i < n_pkts; ++i) {
456                 tx_mbuf[i] = mbufs[i];
457         }
458         return 0;
459 }
460
461 int tx_pkt_no_drop_hw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
462 {
463         int ret = 0;
464         buf_pkt_all(tbase, mbufs, n_pkts, out);
465
466         const uint8_t nb_bufs = tbase->tx_params_hw.nb_txports;
467         uint16_t prod, cons;
468
469         for (uint8_t i = 0; i < nb_bufs; ++i) {
470                 prod = tbase->ws_mbuf->idx[i].prod;
471                 cons = tbase->ws_mbuf->idx[i].cons;
472
473                 if (((uint16_t)(prod - cons)) >= MAX_PKT_BURST) {
474                         tbase->flags &= ~FLAG_TX_FLUSH;
475                         tbase->ws_mbuf->idx[i].cons = cons + MAX_PKT_BURST;
476                         ret+= txhw_no_drop(&tbase->tx_params_hw.tx_port_queue[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), MAX_PKT_BURST, tbase);
477                 }
478         }
479         return ret;
480 }
481
482 int tx_pkt_no_drop_sw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
483 {
484         int ret = 0;
485         buf_pkt_all(tbase, mbufs, n_pkts, out);
486
487         const uint8_t nb_bufs = tbase->tx_params_sw.nb_txrings;
488         uint16_t prod, cons;
489
490         for (uint8_t i = 0; i < nb_bufs; ++i) {
491                 prod = tbase->ws_mbuf->idx[i].prod;
492                 cons = tbase->ws_mbuf->idx[i].cons;
493
494                 if (((uint16_t)(prod - cons)) >= MAX_PKT_BURST) {
495                         tbase->flags &= ~FLAG_TX_FLUSH;
496                         tbase->ws_mbuf->idx[i].cons = cons + MAX_PKT_BURST;
497                         ret += ring_enq_no_drop(tbase->tx_params_sw.tx_rings[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), MAX_PKT_BURST, tbase);
498                 }
499         }
500         return ret;
501 }
502
503 int tx_pkt_hw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
504 {
505         int ret = 0;
506         buf_pkt_all(tbase, mbufs, n_pkts, out);
507
508         const uint8_t nb_bufs = tbase->tx_params_hw.nb_txports;
509         uint16_t prod, cons;
510
511         for (uint8_t i = 0; i < nb_bufs; ++i) {
512                 prod = tbase->ws_mbuf->idx[i].prod;
513                 cons = tbase->ws_mbuf->idx[i].cons;
514
515                 if (((uint16_t)(prod - cons)) >= MAX_PKT_BURST) {
516                         tbase->flags &= ~FLAG_TX_FLUSH;
517                         tbase->ws_mbuf->idx[i].cons = cons + MAX_PKT_BURST;
518                         ret += txhw_drop(&tbase->tx_params_hw.tx_port_queue[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), MAX_PKT_BURST, tbase);
519                 }
520         }
521         return ret;
522 }
523
524 int tx_pkt_sw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
525 {
526         int ret = 0;
527         buf_pkt_all(tbase, mbufs, n_pkts, out);
528
529         const uint8_t nb_bufs = tbase->tx_params_sw.nb_txrings;
530         uint16_t prod, cons;
531         for (uint8_t i = 0; i < nb_bufs; ++i) {
532                 prod = tbase->ws_mbuf->idx[i].prod;
533                 cons = tbase->ws_mbuf->idx[i].cons;
534
535                 if (((uint16_t)(prod - cons)) >= MAX_PKT_BURST) {
536                         tbase->flags &= ~FLAG_TX_FLUSH;
537                         tbase->ws_mbuf->idx[i].cons = cons + MAX_PKT_BURST;
538                         ret+= ring_enq_drop(tbase->tx_params_sw.tx_rings[i], tbase->ws_mbuf->mbuf[i] + (cons & WS_MBUF_MASK), MAX_PKT_BURST, tbase);
539                 }
540         }
541         return ret;
542 }
543
544 int tx_pkt_trace(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
545 {
546         int ret = 0;
547         if (tbase->aux->task_rt_dump.cur_trace == 0) {
548                 // No packet received since dumping...
549                 // So the transmitted packets should not be linked to received packets
550                 tbase->aux->task_rt_dump.n_print_tx = tbase->aux->task_rt_dump.n_trace;
551                 tbase->aux->task_rt_dump.n_trace = 0;
552                 task_base_del_rx_pkt_function(tbase, rx_pkt_trace);
553                 return tx_pkt_dump(tbase, mbufs, n_pkts, out);
554         }
555         plog_info("Tracing %d pkts\n", tbase->aux->task_rt_dump.cur_trace);
556
557         for (uint32_t i = 0; i < tbase->aux->task_rt_dump.cur_trace; ++i) {
558                 struct rte_mbuf tmp;
559                 /* For each packet being transmitted, find which
560                    buffer represent the packet as it was before
561                    processing. */
562                 uint32_t j = 0;
563                 uint32_t len = sizeof(tbase->aux->task_rt_dump.pkt_mbuf_addr)/sizeof(tbase->aux->task_rt_dump.pkt_mbuf_addr[0]);
564                 for (;j < len; ++j) {
565                         if (tbase->aux->task_rt_dump.pkt_mbuf_addr[j] == mbufs[i])
566                                 break;
567                 }
568                 if (j == len) {
569                         plog_info("Trace RX: missing!\n");
570                 }
571                 else {
572 #if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0)
573                         tmp.data_off = 0;
574 #endif
575                         rte_pktmbuf_data_len(&tmp) = tbase->aux->task_rt_dump.pkt_cpy_len[j];
576                         rte_pktmbuf_pkt_len(&tmp) = tbase->aux->task_rt_dump.pkt_cpy_len[j];
577                         tmp.buf_addr = tbase->aux->task_rt_dump.pkt_cpy[j];
578                         plogd_info(&tmp, "Trace RX: ");
579                 }
580
581                 if (out) {
582                         if (out[i] != 0xFF)
583                                 plogd_info(mbufs[i], "Trace TX[%d]: ", out[i]);
584                         else
585                                 plogd_info(mbufs[i], "Trace Dropped: ");
586                 } else
587                         plogd_info(mbufs[i], "Trace TX: ");
588         }
589         ret = tbase->aux->tx_pkt_orig(tbase, mbufs, n_pkts, out);
590
591         /* Unset by TX when n_trace = 0 */
592         if (0 == tbase->aux->task_rt_dump.n_trace) {
593                 tbase->tx_pkt = tbase->aux->tx_pkt_orig;
594                 tbase->aux->tx_pkt_orig = NULL;
595                 task_base_del_rx_pkt_function(tbase, rx_pkt_trace);
596         }
597         return ret;
598 }
599
600 int tx_pkt_dump(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
601 {
602         uint32_t n_dump = tbase->aux->task_rt_dump.n_print_tx;
603         int ret = 0;
604
605         n_dump = n_pkts < n_dump? n_pkts : n_dump;
606         for (uint32_t i = 0; i < n_dump; ++i) {
607                 if (out)
608                         plogd_info(mbufs[i], "TX[%d]: ", out[i]);
609                 else
610                         plogd_info(mbufs[i], "TX: ");
611         }
612         tbase->aux->task_rt_dump.n_print_tx -= n_dump;
613
614         ret = tbase->aux->tx_pkt_orig(tbase, mbufs, n_pkts, out);
615
616         if (0 == tbase->aux->task_rt_dump.n_print_tx) {
617                 tbase->tx_pkt = tbase->aux->tx_pkt_orig;
618                 tbase->aux->tx_pkt_orig = NULL;
619         }
620         return ret;
621 }
622
623 /* Gather the distribution of the number of packets that have been
624    xmitted from one TX call. Since the value is only modified by the
625    task that xmits the packet, no atomic operation is needed. */
626 int tx_pkt_distr(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
627 {
628         tbase->aux->tx_bucket[n_pkts]++;
629         return tbase->aux->tx_pkt_orig(tbase, mbufs, n_pkts, out);
630 }
631
632 int tx_pkt_bw(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
633 {
634         uint32_t tx_bytes = 0;
635         uint32_t drop_bytes = 0;
636
637         for (uint16_t i = 0; i < n_pkts; ++i) {
638                 if (!out || out[i] < OUT_HANDLED)
639                         tx_bytes += mbuf_wire_size(mbufs[i]);
640                 else
641                         drop_bytes += mbuf_wire_size(mbufs[i]);
642         }
643
644         TASK_STATS_ADD_TX_BYTES(&tbase->aux->stats, tx_bytes);
645         TASK_STATS_ADD_DROP_BYTES(&tbase->aux->stats, drop_bytes);
646         return tbase->aux->tx_pkt_orig(tbase, mbufs, n_pkts, out);
647 }
648
649 int tx_pkt_drop_all(struct task_base *tbase, struct rte_mbuf **mbufs, uint16_t n_pkts, uint8_t *out)
650 {
651         for (uint16_t j = 0; j < n_pkts; ++j) {
652                 rte_pktmbuf_free(mbufs[j]);
653         }
654         if (out == NULL)
655                 TASK_STATS_ADD_DROP_HANDLED(&tbase->aux->stats, n_pkts);
656         else {
657                 for (uint16_t j = 0; j < n_pkts; ++j) {
658                         if (out[j] == OUT_HANDLED)
659                                 TASK_STATS_ADD_DROP_HANDLED(&tbase->aux->stats, 1);
660                         else
661                                 TASK_STATS_ADD_DROP_DISCARD(&tbase->aux->stats, 1);
662                 }
663         }
664         return n_pkts;
665 }