build: allow recursive disabling of libraries
[dpdk.git] / app / test-pmd / txonly.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4
5 #include <stdarg.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include <errno.h>
9 #include <stdint.h>
10 #include <unistd.h>
11 #include <inttypes.h>
12
13 #include <sys/queue.h>
14 #include <sys/stat.h>
15
16 #include <rte_common.h>
17 #include <rte_byteorder.h>
18 #include <rte_log.h>
19 #include <rte_debug.h>
20 #include <rte_cycles.h>
21 #include <rte_memory.h>
22 #include <rte_memcpy.h>
23 #include <rte_launch.h>
24 #include <rte_eal.h>
25 #include <rte_per_lcore.h>
26 #include <rte_lcore.h>
27 #include <rte_branch_prediction.h>
28 #include <rte_mempool.h>
29 #include <rte_mbuf.h>
30 #include <rte_interrupts.h>
31 #include <rte_pci.h>
32 #include <rte_ether.h>
33 #include <rte_ethdev.h>
34 #include <rte_ip.h>
35 #include <rte_tcp.h>
36 #include <rte_udp.h>
37 #include <rte_string_fns.h>
38 #include <rte_flow.h>
39
40 #include "testpmd.h"
41
42 struct tx_timestamp {
43         rte_be32_t signature;
44         rte_be16_t pkt_idx;
45         rte_be16_t queue_idx;
46         rte_be64_t ts;
47 };
48
49 /* use RFC863 Discard Protocol */
50 uint16_t tx_udp_src_port = 9;
51 uint16_t tx_udp_dst_port = 9;
52
53 /* use RFC5735 / RFC2544 reserved network test addresses */
54 uint32_t tx_ip_src_addr = (198U << 24) | (18 << 16) | (0 << 8) | 1;
55 uint32_t tx_ip_dst_addr = (198U << 24) | (18 << 16) | (0 << 8) | 2;
56
57 #define IP_DEFTTL  64   /* from RFC 1340. */
58
59 static struct rte_ipv4_hdr pkt_ip_hdr; /**< IP header of transmitted packets. */
60 RTE_DEFINE_PER_LCORE(uint8_t, _ip_var); /**< IP address variation */
61 static struct rte_udp_hdr pkt_udp_hdr; /**< UDP header of tx packets. */
62 RTE_DEFINE_PER_LCORE(uint64_t, timestamp_qskew);
63                                         /**< Timestamp offset per queue */
64 RTE_DEFINE_PER_LCORE(uint32_t, timestamp_idone); /**< Timestamp init done. */
65
66 static uint64_t timestamp_mask; /**< Timestamp dynamic flag mask */
67 static int32_t timestamp_off; /**< Timestamp dynamic field offset */
68 static bool timestamp_enable; /**< Timestamp enable */
69 static uint32_t timestamp_init_req; /**< Timestamp initialization request. */
70 static uint64_t timestamp_initial[RTE_MAX_ETHPORTS];
71
72 static void
73 copy_buf_to_pkt_segs(void* buf, unsigned len, struct rte_mbuf *pkt,
74                      unsigned offset)
75 {
76         struct rte_mbuf *seg;
77         void *seg_buf;
78         unsigned copy_len;
79
80         seg = pkt;
81         while (offset >= seg->data_len) {
82                 offset -= seg->data_len;
83                 seg = seg->next;
84         }
85         copy_len = seg->data_len - offset;
86         seg_buf = rte_pktmbuf_mtod_offset(seg, char *, offset);
87         while (len > copy_len) {
88                 rte_memcpy(seg_buf, buf, (size_t) copy_len);
89                 len -= copy_len;
90                 buf = ((char*) buf + copy_len);
91                 seg = seg->next;
92                 seg_buf = rte_pktmbuf_mtod(seg, char *);
93                 copy_len = seg->data_len;
94         }
95         rte_memcpy(seg_buf, buf, (size_t) len);
96 }
97
98 static inline void
99 copy_buf_to_pkt(void* buf, unsigned len, struct rte_mbuf *pkt, unsigned offset)
100 {
101         if (offset + len <= pkt->data_len) {
102                 rte_memcpy(rte_pktmbuf_mtod_offset(pkt, char *, offset),
103                         buf, (size_t) len);
104                 return;
105         }
106         copy_buf_to_pkt_segs(buf, len, pkt, offset);
107 }
108
109 static void
110 setup_pkt_udp_ip_headers(struct rte_ipv4_hdr *ip_hdr,
111                          struct rte_udp_hdr *udp_hdr,
112                          uint16_t pkt_data_len)
113 {
114         uint16_t *ptr16;
115         uint32_t ip_cksum;
116         uint16_t pkt_len;
117
118         /*
119          * Initialize UDP header.
120          */
121         pkt_len = (uint16_t) (pkt_data_len + sizeof(struct rte_udp_hdr));
122         udp_hdr->src_port = rte_cpu_to_be_16(tx_udp_src_port);
123         udp_hdr->dst_port = rte_cpu_to_be_16(tx_udp_dst_port);
124         udp_hdr->dgram_len      = RTE_CPU_TO_BE_16(pkt_len);
125         udp_hdr->dgram_cksum    = 0; /* No UDP checksum. */
126
127         /*
128          * Initialize IP header.
129          */
130         pkt_len = (uint16_t) (pkt_len + sizeof(struct rte_ipv4_hdr));
131         ip_hdr->version_ihl   = RTE_IPV4_VHL_DEF;
132         ip_hdr->type_of_service   = 0;
133         ip_hdr->fragment_offset = 0;
134         ip_hdr->time_to_live   = IP_DEFTTL;
135         ip_hdr->next_proto_id = IPPROTO_UDP;
136         ip_hdr->packet_id = 0;
137         ip_hdr->total_length   = RTE_CPU_TO_BE_16(pkt_len);
138         ip_hdr->src_addr = rte_cpu_to_be_32(tx_ip_src_addr);
139         ip_hdr->dst_addr = rte_cpu_to_be_32(tx_ip_dst_addr);
140
141         /*
142          * Compute IP header checksum.
143          */
144         ptr16 = (unaligned_uint16_t*) ip_hdr;
145         ip_cksum = 0;
146         ip_cksum += ptr16[0]; ip_cksum += ptr16[1];
147         ip_cksum += ptr16[2]; ip_cksum += ptr16[3];
148         ip_cksum += ptr16[4];
149         ip_cksum += ptr16[6]; ip_cksum += ptr16[7];
150         ip_cksum += ptr16[8]; ip_cksum += ptr16[9];
151
152         /*
153          * Reduce 32 bit checksum to 16 bits and complement it.
154          */
155         ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) +
156                 (ip_cksum & 0x0000FFFF);
157         if (ip_cksum > 65535)
158                 ip_cksum -= 65535;
159         ip_cksum = (~ip_cksum) & 0x0000FFFF;
160         if (ip_cksum == 0)
161                 ip_cksum = 0xFFFF;
162         ip_hdr->hdr_checksum = (uint16_t) ip_cksum;
163 }
164
165 static inline void
166 update_pkt_header(struct rte_mbuf *pkt, uint32_t total_pkt_len)
167 {
168         struct rte_ipv4_hdr *ip_hdr;
169         struct rte_udp_hdr *udp_hdr;
170         uint16_t pkt_data_len;
171         uint16_t pkt_len;
172
173         pkt_data_len = (uint16_t) (total_pkt_len - (
174                                         sizeof(struct rte_ether_hdr) +
175                                         sizeof(struct rte_ipv4_hdr) +
176                                         sizeof(struct rte_udp_hdr)));
177         /* update UDP packet length */
178         udp_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_udp_hdr *,
179                                 sizeof(struct rte_ether_hdr) +
180                                 sizeof(struct rte_ipv4_hdr));
181         pkt_len = (uint16_t) (pkt_data_len + sizeof(struct rte_udp_hdr));
182         udp_hdr->dgram_len = RTE_CPU_TO_BE_16(pkt_len);
183
184         /* update IP packet length and checksum */
185         ip_hdr = rte_pktmbuf_mtod_offset(pkt, struct rte_ipv4_hdr *,
186                                 sizeof(struct rte_ether_hdr));
187         ip_hdr->hdr_checksum = 0;
188         pkt_len = (uint16_t) (pkt_len + sizeof(struct rte_ipv4_hdr));
189         ip_hdr->total_length = RTE_CPU_TO_BE_16(pkt_len);
190         ip_hdr->hdr_checksum = rte_ipv4_cksum(ip_hdr);
191 }
192
193 static inline bool
194 pkt_burst_prepare(struct rte_mbuf *pkt, struct rte_mempool *mbp,
195                 struct rte_ether_hdr *eth_hdr, const uint16_t vlan_tci,
196                 const uint16_t vlan_tci_outer, const uint64_t ol_flags,
197                 const uint16_t idx, const struct fwd_stream *fs)
198 {
199         struct rte_mbuf *pkt_segs[RTE_MAX_SEGS_PER_PKT];
200         struct rte_mbuf *pkt_seg;
201         uint32_t nb_segs, pkt_len;
202         uint8_t i;
203
204         if (unlikely(tx_pkt_split == TX_PKT_SPLIT_RND))
205                 nb_segs = rte_rand() % tx_pkt_nb_segs + 1;
206         else
207                 nb_segs = tx_pkt_nb_segs;
208
209         if (nb_segs > 1) {
210                 if (rte_mempool_get_bulk(mbp, (void **)pkt_segs, nb_segs - 1))
211                         return false;
212         }
213
214         rte_pktmbuf_reset_headroom(pkt);
215         pkt->data_len = tx_pkt_seg_lengths[0];
216         pkt->ol_flags &= RTE_MBUF_F_EXTERNAL;
217         pkt->ol_flags |= ol_flags;
218         pkt->vlan_tci = vlan_tci;
219         pkt->vlan_tci_outer = vlan_tci_outer;
220         pkt->l2_len = sizeof(struct rte_ether_hdr);
221         pkt->l3_len = sizeof(struct rte_ipv4_hdr);
222
223         pkt_len = pkt->data_len;
224         pkt_seg = pkt;
225         for (i = 1; i < nb_segs; i++) {
226                 pkt_seg->next = pkt_segs[i - 1];
227                 pkt_seg = pkt_seg->next;
228                 pkt_seg->data_len = tx_pkt_seg_lengths[i];
229                 pkt_len += pkt_seg->data_len;
230         }
231         pkt_seg->next = NULL; /* Last segment of packet. */
232         /*
233          * Copy headers in first packet segment(s).
234          */
235         copy_buf_to_pkt(eth_hdr, sizeof(*eth_hdr), pkt, 0);
236         copy_buf_to_pkt(&pkt_ip_hdr, sizeof(pkt_ip_hdr), pkt,
237                         sizeof(struct rte_ether_hdr));
238         if (txonly_multi_flow) {
239                 uint8_t  ip_var = RTE_PER_LCORE(_ip_var);
240                 struct rte_ipv4_hdr *ip_hdr;
241                 uint32_t addr;
242
243                 ip_hdr = rte_pktmbuf_mtod_offset(pkt,
244                                 struct rte_ipv4_hdr *,
245                                 sizeof(struct rte_ether_hdr));
246                 /*
247                  * Generate multiple flows by varying IP src addr. This
248                  * enables packets are well distributed by RSS in
249                  * receiver side if any and txonly mode can be a decent
250                  * packet generator for developer's quick performance
251                  * regression test.
252                  */
253                 addr = (tx_ip_dst_addr | (ip_var++ << 8)) + rte_lcore_id();
254                 ip_hdr->src_addr = rte_cpu_to_be_32(addr);
255                 RTE_PER_LCORE(_ip_var) = ip_var;
256         }
257         copy_buf_to_pkt(&pkt_udp_hdr, sizeof(pkt_udp_hdr), pkt,
258                         sizeof(struct rte_ether_hdr) +
259                         sizeof(struct rte_ipv4_hdr));
260
261         if (unlikely(tx_pkt_split == TX_PKT_SPLIT_RND) || txonly_multi_flow)
262                 update_pkt_header(pkt, pkt_len);
263
264         if (unlikely(timestamp_enable)) {
265                 uint64_t skew = RTE_PER_LCORE(timestamp_qskew);
266                 struct tx_timestamp timestamp_mark;
267
268                 if (unlikely(timestamp_init_req !=
269                                 RTE_PER_LCORE(timestamp_idone))) {
270                         struct rte_eth_dev_info dev_info;
271                         unsigned int txqs_n;
272                         uint64_t phase;
273                         int ret;
274
275                         ret = eth_dev_info_get_print_err(fs->tx_port, &dev_info);
276                         if (ret != 0) {
277                                 TESTPMD_LOG(ERR,
278                                         "Failed to get device info for port %d,"
279                                         "could not finish timestamp init",
280                                         fs->tx_port);
281                                 return false;
282                         }
283                         txqs_n = dev_info.nb_tx_queues;
284                         phase = tx_pkt_times_inter * fs->tx_queue /
285                                          (txqs_n ? txqs_n : 1);
286                         /*
287                          * Initialize the scheduling time phase shift
288                          * depending on queue index.
289                          */
290                         skew = timestamp_initial[fs->tx_port] +
291                                tx_pkt_times_inter + phase;
292                         RTE_PER_LCORE(timestamp_qskew) = skew;
293                         RTE_PER_LCORE(timestamp_idone) = timestamp_init_req;
294                 }
295                 timestamp_mark.pkt_idx = rte_cpu_to_be_16(idx);
296                 timestamp_mark.queue_idx = rte_cpu_to_be_16(fs->tx_queue);
297                 timestamp_mark.signature = rte_cpu_to_be_32(0xBEEFC0DE);
298                 if (unlikely(!idx)) {
299                         skew += tx_pkt_times_inter;
300                         pkt->ol_flags |= timestamp_mask;
301                         *RTE_MBUF_DYNFIELD
302                                 (pkt, timestamp_off, uint64_t *) = skew;
303                         RTE_PER_LCORE(timestamp_qskew) = skew;
304                         timestamp_mark.ts = rte_cpu_to_be_64(skew);
305                 } else if (tx_pkt_times_intra) {
306                         skew += tx_pkt_times_intra;
307                         pkt->ol_flags |= timestamp_mask;
308                         *RTE_MBUF_DYNFIELD
309                                 (pkt, timestamp_off, uint64_t *) = skew;
310                         RTE_PER_LCORE(timestamp_qskew) = skew;
311                         timestamp_mark.ts = rte_cpu_to_be_64(skew);
312                 } else {
313                         timestamp_mark.ts = RTE_BE64(0);
314                 }
315                 copy_buf_to_pkt(&timestamp_mark, sizeof(timestamp_mark), pkt,
316                         sizeof(struct rte_ether_hdr) +
317                         sizeof(struct rte_ipv4_hdr) +
318                         sizeof(pkt_udp_hdr));
319         }
320         /*
321          * Complete first mbuf of packet and append it to the
322          * burst of packets to be transmitted.
323          */
324         pkt->nb_segs = nb_segs;
325         pkt->pkt_len = pkt_len;
326
327         return true;
328 }
329
330 /*
331  * Transmit a burst of multi-segments packets.
332  */
333 static void
334 pkt_burst_transmit(struct fwd_stream *fs)
335 {
336         struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
337         struct rte_port *txp;
338         struct rte_mbuf *pkt;
339         struct rte_mempool *mbp;
340         struct rte_ether_hdr eth_hdr;
341         uint16_t nb_tx;
342         uint16_t nb_pkt;
343         uint16_t vlan_tci, vlan_tci_outer;
344         uint32_t retry;
345         uint64_t ol_flags = 0;
346         uint64_t tx_offloads;
347         uint64_t start_tsc = 0;
348
349         get_start_cycles(&start_tsc);
350
351         mbp = current_fwd_lcore()->mbp;
352         txp = &ports[fs->tx_port];
353         tx_offloads = txp->dev_conf.txmode.offloads;
354         vlan_tci = txp->tx_vlan_id;
355         vlan_tci_outer = txp->tx_vlan_id_outer;
356         if (tx_offloads & RTE_ETH_TX_OFFLOAD_VLAN_INSERT)
357                 ol_flags = RTE_MBUF_F_TX_VLAN;
358         if (tx_offloads & RTE_ETH_TX_OFFLOAD_QINQ_INSERT)
359                 ol_flags |= RTE_MBUF_F_TX_QINQ;
360         if (tx_offloads & RTE_ETH_TX_OFFLOAD_MACSEC_INSERT)
361                 ol_flags |= RTE_MBUF_F_TX_MACSEC;
362
363         /*
364          * Initialize Ethernet header.
365          */
366         rte_ether_addr_copy(&peer_eth_addrs[fs->peer_addr], &eth_hdr.dst_addr);
367         rte_ether_addr_copy(&ports[fs->tx_port].eth_addr, &eth_hdr.src_addr);
368         eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
369
370         if (rte_mempool_get_bulk(mbp, (void **)pkts_burst,
371                                 nb_pkt_per_burst) == 0) {
372                 for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) {
373                         if (unlikely(!pkt_burst_prepare(pkts_burst[nb_pkt], mbp,
374                                                         &eth_hdr, vlan_tci,
375                                                         vlan_tci_outer,
376                                                         ol_flags,
377                                                         nb_pkt, fs))) {
378                                 rte_mempool_put_bulk(mbp,
379                                                 (void **)&pkts_burst[nb_pkt],
380                                                 nb_pkt_per_burst - nb_pkt);
381                                 break;
382                         }
383                 }
384         } else {
385                 for (nb_pkt = 0; nb_pkt < nb_pkt_per_burst; nb_pkt++) {
386                         pkt = rte_mbuf_raw_alloc(mbp);
387                         if (pkt == NULL)
388                                 break;
389                         if (unlikely(!pkt_burst_prepare(pkt, mbp, &eth_hdr,
390                                                         vlan_tci,
391                                                         vlan_tci_outer,
392                                                         ol_flags,
393                                                         nb_pkt, fs))) {
394                                 rte_pktmbuf_free(pkt);
395                                 break;
396                         }
397                         pkts_burst[nb_pkt] = pkt;
398                 }
399         }
400
401         if (nb_pkt == 0)
402                 return;
403
404         nb_tx = rte_eth_tx_burst(fs->tx_port, fs->tx_queue, pkts_burst, nb_pkt);
405
406         /*
407          * Retry if necessary
408          */
409         if (unlikely(nb_tx < nb_pkt) && fs->retry_enabled) {
410                 retry = 0;
411                 while (nb_tx < nb_pkt && retry++ < burst_tx_retry_num) {
412                         rte_delay_us(burst_tx_delay_time);
413                         nb_tx += rte_eth_tx_burst(fs->tx_port, fs->tx_queue,
414                                         &pkts_burst[nb_tx], nb_pkt - nb_tx);
415                 }
416         }
417         fs->tx_packets += nb_tx;
418
419         if (txonly_multi_flow)
420                 RTE_PER_LCORE(_ip_var) -= nb_pkt - nb_tx;
421
422         inc_tx_burst_stats(fs, nb_tx);
423         if (unlikely(nb_tx < nb_pkt)) {
424                 if (verbose_level > 0 && fs->fwd_dropped == 0)
425                         printf("port %d tx_queue %d - drop "
426                                "(nb_pkt:%u - nb_tx:%u)=%u packets\n",
427                                fs->tx_port, fs->tx_queue,
428                                (unsigned) nb_pkt, (unsigned) nb_tx,
429                                (unsigned) (nb_pkt - nb_tx));
430                 fs->fwd_dropped += (nb_pkt - nb_tx);
431                 do {
432                         rte_pktmbuf_free(pkts_burst[nb_tx]);
433                 } while (++nb_tx < nb_pkt);
434         }
435
436         get_end_cycles(fs, start_tsc);
437 }
438
439 static int
440 tx_only_begin(portid_t pi)
441 {
442         uint16_t pkt_hdr_len, pkt_data_len;
443         int dynf;
444
445         pkt_hdr_len = (uint16_t)(sizeof(struct rte_ether_hdr) +
446                                  sizeof(struct rte_ipv4_hdr) +
447                                  sizeof(struct rte_udp_hdr));
448         pkt_data_len = tx_pkt_length - pkt_hdr_len;
449
450         if ((tx_pkt_split == TX_PKT_SPLIT_RND || txonly_multi_flow) &&
451             tx_pkt_seg_lengths[0] < pkt_hdr_len) {
452                 TESTPMD_LOG(ERR,
453                             "Random segment number or multiple flow is enabled, "
454                             "but tx_pkt_seg_lengths[0] %u < %u (needed)\n",
455                             tx_pkt_seg_lengths[0], pkt_hdr_len);
456                 return -EINVAL;
457         }
458
459         setup_pkt_udp_ip_headers(&pkt_ip_hdr, &pkt_udp_hdr, pkt_data_len);
460
461         timestamp_enable = false;
462         timestamp_mask = 0;
463         timestamp_off = -1;
464         RTE_PER_LCORE(timestamp_qskew) = 0;
465         dynf = rte_mbuf_dynflag_lookup
466                                 (RTE_MBUF_DYNFLAG_TX_TIMESTAMP_NAME, NULL);
467         if (dynf >= 0)
468                 timestamp_mask = 1ULL << dynf;
469         dynf = rte_mbuf_dynfield_lookup
470                                 (RTE_MBUF_DYNFIELD_TIMESTAMP_NAME, NULL);
471         if (dynf >= 0)
472                 timestamp_off = dynf;
473         timestamp_enable = tx_pkt_times_inter &&
474                            timestamp_mask &&
475                            timestamp_off >= 0 &&
476                            !rte_eth_read_clock(pi, &timestamp_initial[pi]);
477
478         if (timestamp_enable) {
479                 pkt_hdr_len += sizeof(struct tx_timestamp);
480
481                 if (tx_pkt_split == TX_PKT_SPLIT_RND) {
482                         if (tx_pkt_seg_lengths[0] < pkt_hdr_len) {
483                                 TESTPMD_LOG(ERR,
484                                             "Time stamp and random segment number are enabled, "
485                                             "but tx_pkt_seg_lengths[0] %u < %u (needed)\n",
486                                             tx_pkt_seg_lengths[0], pkt_hdr_len);
487                                 return -EINVAL;
488                         }
489                 } else {
490                         uint16_t total = 0;
491                         uint8_t i;
492
493                         for (i = 0; i < tx_pkt_nb_segs; i++) {
494                                 total += tx_pkt_seg_lengths[i];
495                                 if (total >= pkt_hdr_len)
496                                         break;
497                         }
498
499                         if (total < pkt_hdr_len) {
500                                 TESTPMD_LOG(ERR,
501                                             "Not enough Tx segment space for time stamp info, "
502                                             "total %u < %u (needed)\n",
503                                             total, pkt_hdr_len);
504                                 return -EINVAL;
505                         }
506                 }
507                 timestamp_init_req++;
508         }
509
510         /* Make sure all settings are visible on forwarding cores.*/
511         rte_wmb();
512         return 0;
513 }
514
515 struct fwd_engine tx_only_engine = {
516         .fwd_mode_name  = "txonly",
517         .port_fwd_begin = tx_only_begin,
518         .port_fwd_end   = NULL,
519         .packet_fwd     = pkt_burst_transmit,
520 };