1a4a50d2a69dbca4d690eb58fe6320759b896746
[dpdk.git] / app / test-flow-perf / main.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2020 Mellanox Technologies, Ltd
3  *
4  * This file contain the application main file
5  * This application provides the user the ability to test the
6  * insertion rate for specific rte_flow rule under stress state ~4M rule/
7  *
8  * Then it will also provide packet per second measurement after installing
9  * all rules, the user may send traffic to test the PPS that match the rules
10  * after all rules are installed, to check performance or functionality after
11  * the stress.
12  *
13  * The flows insertion will go for all ports first, then it will print the
14  * results, after that the application will go into forwarding packets mode
15  * it will start receiving traffic if any and then forwarding it back and
16  * gives packet per second measurement.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdint.h>
23 #include <inttypes.h>
24 #include <stdarg.h>
25 #include <errno.h>
26 #include <getopt.h>
27 #include <stdbool.h>
28 #include <sys/time.h>
29 #include <signal.h>
30
31 #include <rte_malloc.h>
32 #include <rte_mempool.h>
33 #include <rte_mbuf.h>
34 #include <rte_ethdev.h>
35 #include <rte_flow.h>
36
37 #include "config.h"
38 #include "flow_gen.h"
39
40 #define MAX_ITERATIONS             100
41 #define DEFAULT_RULES_COUNT    4000000
42 #define DEFAULT_ITERATION       100000
43
44 struct rte_flow *flow;
45 static uint8_t flow_group;
46
47 static uint64_t flow_items;
48 static uint64_t flow_actions;
49 static uint64_t flow_attrs;
50 static volatile bool force_quit;
51 static bool dump_iterations;
52 static struct rte_mempool *mbuf_mp;
53 static uint32_t nb_lcores;
54 static uint32_t flows_count;
55 static uint32_t iterations_number;
56 static uint32_t hairpinq;
57
58 static void
59 usage(char *progname)
60 {
61         printf("\nusage: %s\n", progname);
62         printf("\nControl configurations:\n");
63         printf("  --flows-count=N: to set the number of needed"
64                 " flows to insert, default is 4,000,000\n");
65         printf("  --dump-iterations: To print rates for each"
66                 " iteration\n");
67
68         printf("To set flow attributes:\n");
69         printf("  --ingress: set ingress attribute in flows\n");
70         printf("  --egress: set egress attribute in flows\n");
71         printf("  --transfer: set transfer attribute in flows\n");
72         printf("  --group=N: set group for all flows,"
73                 " default is 0\n");
74
75         printf("To set flow items:\n");
76         printf("  --ether: add ether layer in flow items\n");
77         printf("  --vlan: add vlan layer in flow items\n");
78         printf("  --ipv4: add ipv4 layer in flow items\n");
79         printf("  --ipv6: add ipv6 layer in flow items\n");
80         printf("  --tcp: add tcp layer in flow items\n");
81         printf("  --udp: add udp layer in flow items\n");
82         printf("  --vxlan: add vxlan layer in flow items\n");
83         printf("  --vxlan-gpe: add vxlan-gpe layer in flow items\n");
84         printf("  --gre: add gre layer in flow items\n");
85         printf("  --geneve: add geneve layer in flow items\n");
86         printf("  --gtp: add gtp layer in flow items\n");
87         printf("  --meta: add meta layer in flow items\n");
88         printf("  --tag: add tag layer in flow items\n");
89
90         printf("To set flow actions:\n");
91         printf("  --port-id: add port-id action in flow actions\n");
92         printf("  --rss: add rss action in flow actions\n");
93         printf("  --queue: add queue action in flow actions\n");
94         printf("  --jump: add jump action in flow actions\n");
95         printf("  --mark: add mark action in flow actions\n");
96         printf("  --count: add count action in flow actions\n");
97         printf("  --set-meta: add set meta action in flow actions\n");
98         printf("  --set-tag: add set tag action in flow actions\n");
99         printf("  --drop: add drop action in flow actions\n");
100         printf("  --hairpin-queue=N: add hairpin-queue action in flow actions\n");
101         printf("  --hairpin-rss=N: add hairping-rss action in flow actions\n");
102 }
103
104 static void
105 args_parse(int argc, char **argv)
106 {
107         char **argvopt;
108         int n, opt;
109         int opt_idx;
110         size_t i;
111
112         static const struct option_dict {
113                 const char *str;
114                 const uint64_t mask;
115                 uint64_t *bitmap;
116         } flow_options[] = {
117                 {
118                         .str = "ether",
119                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_ETH),
120                         .bitmap = &flow_items
121                 },
122                 {
123                         .str = "ipv4",
124                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_IPV4),
125                         .bitmap = &flow_items
126                 },
127                 {
128                         .str = "ipv6",
129                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_IPV6),
130                         .bitmap = &flow_items
131                 },
132                 {
133                         .str = "vlan",
134                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_VLAN),
135                         .bitmap = &flow_items
136                 },
137                 {
138                         .str = "tcp",
139                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_TCP),
140                         .bitmap = &flow_items
141                 },
142                 {
143                         .str = "udp",
144                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_UDP),
145                         .bitmap = &flow_items
146                 },
147                 {
148                         .str = "vxlan",
149                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_VXLAN),
150                         .bitmap = &flow_items
151                 },
152                 {
153                         .str = "vxlan-gpe",
154                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_VXLAN_GPE),
155                         .bitmap = &flow_items
156                 },
157                 {
158                         .str = "gre",
159                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_GRE),
160                         .bitmap = &flow_items
161                 },
162                 {
163                         .str = "geneve",
164                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_GENEVE),
165                         .bitmap = &flow_items
166                 },
167                 {
168                         .str = "gtp",
169                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_GTP),
170                         .bitmap = &flow_items
171                 },
172                 {
173                         .str = "meta",
174                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_META),
175                         .bitmap = &flow_items
176                 },
177                 {
178                         .str = "tag",
179                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_TAG),
180                         .bitmap = &flow_items
181                 },
182                 {
183                         .str = "ingress",
184                         .mask = INGRESS,
185                         .bitmap = &flow_attrs
186                 },
187                 {
188                         .str = "egress",
189                         .mask = EGRESS,
190                         .bitmap = &flow_attrs
191                 },
192                 {
193                         .str = "transfer",
194                         .mask = TRANSFER,
195                         .bitmap = &flow_attrs
196                 },
197                 {
198                         .str = "port-id",
199                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_PORT_ID),
200                         .bitmap = &flow_actions
201                 },
202                 {
203                         .str = "rss",
204                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_RSS),
205                         .bitmap = &flow_actions
206                 },
207                 {
208                         .str = "queue",
209                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_QUEUE),
210                         .bitmap = &flow_actions
211                 },
212                 {
213                         .str = "jump",
214                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_JUMP),
215                         .bitmap = &flow_actions
216                 },
217                 {
218                         .str = "mark",
219                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_MARK),
220                         .bitmap = &flow_actions
221                 },
222                 {
223                         .str = "count",
224                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_COUNT),
225                         .bitmap = &flow_actions
226                 },
227                 {
228                         .str = "set-meta",
229                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_SET_META),
230                         .bitmap = &flow_actions
231                 },
232                 {
233                         .str = "set-tag",
234                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_SET_TAG),
235                         .bitmap = &flow_actions
236                 },
237                 {
238                         .str = "drop",
239                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_DROP),
240                         .bitmap = &flow_actions
241                 }
242         };
243
244         static const struct option lgopts[] = {
245                 /* Control */
246                 { "help",                       0, 0, 0 },
247                 { "flows-count",                1, 0, 0 },
248                 { "dump-iterations",            0, 0, 0 },
249                 /* Attributes */
250                 { "ingress",                    0, 0, 0 },
251                 { "egress",                     0, 0, 0 },
252                 { "transfer",                   0, 0, 0 },
253                 { "group",                      1, 0, 0 },
254                 /* Items */
255                 { "ether",                      0, 0, 0 },
256                 { "vlan",                       0, 0, 0 },
257                 { "ipv4",                       0, 0, 0 },
258                 { "ipv6",                       0, 0, 0 },
259                 { "tcp",                        0, 0, 0 },
260                 { "udp",                        0, 0, 0 },
261                 { "vxlan",                      0, 0, 0 },
262                 { "vxlan-gpe",                  0, 0, 0 },
263                 { "gre",                        0, 0, 0 },
264                 { "geneve",                     0, 0, 0 },
265                 { "gtp",                        0, 0, 0 },
266                 { "meta",                       0, 0, 0 },
267                 { "tag",                        0, 0, 0 },
268                 /* Actions */
269                 { "port-id",                    0, 0, 0 },
270                 { "rss",                        0, 0, 0 },
271                 { "queue",                      0, 0, 0 },
272                 { "jump",                       0, 0, 0 },
273                 { "mark",                       0, 0, 0 },
274                 { "count",                      0, 0, 0 },
275                 { "set-meta",                   0, 0, 0 },
276                 { "set-tag",                    0, 0, 0 },
277                 { "drop",                       0, 0, 0 },
278                 { "hairpin-queue",              1, 0, 0 },
279                 { "hairpin-rss",                1, 0, 0 },
280         };
281
282         flow_items = 0;
283         flow_actions = 0;
284         flow_attrs = 0;
285         hairpinq = 0;
286         argvopt = argv;
287
288         printf(":: Flow -> ");
289         while ((opt = getopt_long(argc, argvopt, "",
290                                 lgopts, &opt_idx)) != EOF) {
291                 switch (opt) {
292                 case 0:
293                         if (strcmp(lgopts[opt_idx].name, "help") == 0) {
294                                 usage(argv[0]);
295                                 rte_exit(EXIT_SUCCESS, "Displayed help\n");
296                         }
297
298                         if (strcmp(lgopts[opt_idx].name, "group") == 0) {
299                                 n = atoi(optarg);
300                                 if (n >= 0)
301                                         flow_group = n;
302                                 else
303                                         rte_exit(EXIT_SUCCESS,
304                                                 "flow group should be >= 0\n");
305                                 printf("group %d ", flow_group);
306                         }
307
308                         for (i = 0; i < RTE_DIM(flow_options); i++)
309                                 if (strcmp(lgopts[opt_idx].name,
310                                                 flow_options[i].str) == 0) {
311                                         *flow_options[i].bitmap |=
312                                                 flow_options[i].mask;
313                                         printf("%s / ", flow_options[i].str);
314                                 }
315
316                         if (strcmp(lgopts[opt_idx].name,
317                                         "hairpin-rss") == 0) {
318                                 n = atoi(optarg);
319                                 if (n > 0)
320                                         hairpinq = n;
321                                 else
322                                         rte_exit(EXIT_SUCCESS,
323                                                 "Hairpin queues should be > 0\n");
324
325                                 flow_actions |= HAIRPIN_RSS_ACTION;
326                                 printf("hairpin-rss / ");
327                         }
328                         if (strcmp(lgopts[opt_idx].name,
329                                         "hairpin-queue") == 0) {
330                                 n = atoi(optarg);
331                                 if (n > 0)
332                                         hairpinq = n;
333                                 else
334                                         rte_exit(EXIT_SUCCESS,
335                                                 "Hairpin queues should be > 0\n");
336
337                                 flow_actions |= HAIRPIN_QUEUE_ACTION;
338                                 printf("hairpin-queue / ");
339                         }
340
341                         /* Control */
342                         if (strcmp(lgopts[opt_idx].name,
343                                         "flows-count") == 0) {
344                                 n = atoi(optarg);
345                                 if (n > (int) iterations_number)
346                                         flows_count = n;
347                                 else {
348                                         printf("\n\nflows_count should be > %d\n",
349                                                 iterations_number);
350                                         rte_exit(EXIT_SUCCESS, " ");
351                                 }
352                         }
353                         if (strcmp(lgopts[opt_idx].name,
354                                         "dump-iterations") == 0)
355                                 dump_iterations = true;
356                         break;
357                 default:
358                         fprintf(stderr, "Invalid option: %s\n", argv[optind]);
359                         usage(argv[0]);
360                         rte_exit(EXIT_SUCCESS, "Invalid option\n");
361                         break;
362                 }
363         }
364         printf("end_flow\n");
365 }
366
367 static void
368 print_flow_error(struct rte_flow_error error)
369 {
370         printf("Flow can't be created %d message: %s\n",
371                 error.type,
372                 error.message ? error.message : "(no stated reason)");
373 }
374
375 static inline void
376 flows_handler(void)
377 {
378         struct rte_flow_error error;
379         clock_t start_iter, end_iter;
380         double cpu_time_used;
381         double flows_rate;
382         double cpu_time_per_iter[MAX_ITERATIONS];
383         double delta;
384         uint16_t nr_ports;
385         uint32_t i;
386         int port_id;
387         int iter_id;
388
389         nr_ports = rte_eth_dev_count_avail();
390
391         for (i = 0; i < MAX_ITERATIONS; i++)
392                 cpu_time_per_iter[i] = -1;
393
394         if (iterations_number > flows_count)
395                 iterations_number = flows_count;
396
397         printf(":: Flows Count per port: %d\n", flows_count);
398
399         for (port_id = 0; port_id < nr_ports; port_id++) {
400                 cpu_time_used = 0;
401                 if (flow_group > 0) {
402                         /*
403                          * Create global rule to jump into flow_group,
404                          * this way the app will avoid the default rules.
405                          *
406                          * Global rule:
407                          * group 0 eth / end actions jump group <flow_group>
408                          *
409                          */
410                         flow = generate_flow(port_id, 0, flow_attrs,
411                                 FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_ETH),
412                                 FLOW_ITEM_MASK(RTE_FLOW_ACTION_TYPE_JUMP),
413                                 flow_group, 0, 0, &error);
414
415                         if (flow == NULL) {
416                                 print_flow_error(error);
417                                 rte_exit(EXIT_FAILURE, "error in creating flow");
418                         }
419                 }
420
421                 /* Insertion Rate */
422                 printf("Flows insertion on port = %d\n", port_id);
423                 start_iter = clock();
424                 for (i = 0; i < flows_count; i++) {
425                         flow = generate_flow(port_id, flow_group,
426                                 flow_attrs, flow_items, flow_actions,
427                                 JUMP_ACTION_TABLE, i, hairpinq, &error);
428
429                         if (force_quit)
430                                 i = flows_count;
431
432                         if (!flow) {
433                                 print_flow_error(error);
434                                 rte_exit(EXIT_FAILURE, "error in creating flow");
435                         }
436
437                         if (i && !((i + 1) % iterations_number)) {
438                                 /* Save the insertion rate of each iter */
439                                 end_iter = clock();
440                                 delta = (double) (end_iter - start_iter);
441                                 iter_id = ((i + 1) / iterations_number) - 1;
442                                 cpu_time_per_iter[iter_id] =
443                                         delta / CLOCKS_PER_SEC;
444                                 cpu_time_used += cpu_time_per_iter[iter_id];
445                                 start_iter = clock();
446                         }
447                 }
448
449                 /* Iteration rate per iteration */
450                 if (dump_iterations)
451                         for (i = 0; i < MAX_ITERATIONS; i++) {
452                                 if (cpu_time_per_iter[i] == -1)
453                                         continue;
454                                 delta = (double)(iterations_number /
455                                         cpu_time_per_iter[i]);
456                                 flows_rate = delta / 1000;
457                                 printf(":: Iteration #%d: %d flows "
458                                         "in %f sec[ Rate = %f K/Sec ]\n",
459                                         i, iterations_number,
460                                         cpu_time_per_iter[i], flows_rate);
461                         }
462
463                 /* Insertion rate for all flows */
464                 flows_rate = ((double) (flows_count / cpu_time_used) / 1000);
465                 printf("\n:: Total flow insertion rate -> %f K/Sec\n",
466                                                 flows_rate);
467                 printf(":: The time for creating %d in flows %f seconds\n",
468                                                 flows_count, cpu_time_used);
469         }
470 }
471
472 static void
473 signal_handler(int signum)
474 {
475         if (signum == SIGINT || signum == SIGTERM) {
476                 printf("\n\nSignal %d received, preparing to exit...\n",
477                                         signum);
478                 printf("Error: Stats are wrong due to sudden signal!\n\n");
479                 force_quit = true;
480         }
481 }
482
483 static void
484 init_port(void)
485 {
486         int ret;
487         uint16_t std_queue;
488         uint16_t hairpin_q;
489         uint16_t port_id;
490         uint16_t nr_ports;
491         uint16_t nr_queues;
492         struct rte_eth_hairpin_conf hairpin_conf = {
493                 .peer_count = 1,
494         };
495         struct rte_eth_conf port_conf = {
496                 .rx_adv_conf = {
497                         .rss_conf.rss_hf =
498                                 GET_RSS_HF(),
499                 }
500         };
501         struct rte_eth_txconf txq_conf;
502         struct rte_eth_rxconf rxq_conf;
503         struct rte_eth_dev_info dev_info;
504
505         nr_queues = RXQ_NUM;
506         if (hairpinq != 0)
507                 nr_queues = RXQ_NUM + hairpinq;
508
509         nr_ports = rte_eth_dev_count_avail();
510         if (nr_ports == 0)
511                 rte_exit(EXIT_FAILURE, "Error: no port detected\n");
512
513         mbuf_mp = rte_pktmbuf_pool_create("mbuf_pool",
514                                         TOTAL_MBUF_NUM, MBUF_CACHE_SIZE,
515                                         0, MBUF_SIZE,
516                                         rte_socket_id());
517         if (mbuf_mp == NULL)
518                 rte_exit(EXIT_FAILURE, "Error: can't init mbuf pool\n");
519
520         for (port_id = 0; port_id < nr_ports; port_id++) {
521                 ret = rte_eth_dev_info_get(port_id, &dev_info);
522                 if (ret != 0)
523                         rte_exit(EXIT_FAILURE,
524                                 "Error during getting device"
525                                 " (port %u) info: %s\n",
526                                 port_id, strerror(-ret));
527
528                 port_conf.txmode.offloads &= dev_info.tx_offload_capa;
529                 port_conf.rxmode.offloads &= dev_info.rx_offload_capa;
530
531                 printf(":: initializing port: %d\n", port_id);
532
533                 ret = rte_eth_dev_configure(port_id, nr_queues,
534                                 nr_queues, &port_conf);
535                 if (ret < 0)
536                         rte_exit(EXIT_FAILURE,
537                                 ":: cannot configure device: err=%d, port=%u\n",
538                                 ret, port_id);
539
540                 rxq_conf = dev_info.default_rxconf;
541                 for (std_queue = 0; std_queue < RXQ_NUM; std_queue++) {
542                         ret = rte_eth_rx_queue_setup(port_id, std_queue, NR_RXD,
543                                         rte_eth_dev_socket_id(port_id),
544                                         &rxq_conf,
545                                         mbuf_mp);
546                         if (ret < 0)
547                                 rte_exit(EXIT_FAILURE,
548                                         ":: Rx queue setup failed: err=%d, port=%u\n",
549                                         ret, port_id);
550                 }
551
552                 txq_conf = dev_info.default_txconf;
553                 for (std_queue = 0; std_queue < TXQ_NUM; std_queue++) {
554                         ret = rte_eth_tx_queue_setup(port_id, std_queue, NR_TXD,
555                                         rte_eth_dev_socket_id(port_id),
556                                         &txq_conf);
557                         if (ret < 0)
558                                 rte_exit(EXIT_FAILURE,
559                                         ":: Tx queue setup failed: err=%d, port=%u\n",
560                                         ret, port_id);
561                 }
562
563                 /* Catch all packets from traffic generator. */
564                 ret = rte_eth_promiscuous_enable(port_id);
565                 if (ret != 0)
566                         rte_exit(EXIT_FAILURE,
567                                 ":: promiscuous mode enable failed: err=%s, port=%u\n",
568                                 rte_strerror(-ret), port_id);
569
570                 if (hairpinq != 0) {
571                         for (hairpin_q = RXQ_NUM, std_queue = 0;
572                                         std_queue < nr_queues;
573                                         hairpin_q++, std_queue++) {
574                                 hairpin_conf.peers[0].port = port_id;
575                                 hairpin_conf.peers[0].queue =
576                                         std_queue + TXQ_NUM;
577                                 ret = rte_eth_rx_hairpin_queue_setup(
578                                                 port_id, hairpin_q,
579                                                 NR_RXD, &hairpin_conf);
580                                 if (ret != 0)
581                                         rte_exit(EXIT_FAILURE,
582                                                 ":: Hairpin rx queue setup failed: err=%d, port=%u\n",
583                                                 ret, port_id);
584                         }
585
586                         for (hairpin_q = TXQ_NUM, std_queue = 0;
587                                         std_queue < nr_queues;
588                                         hairpin_q++, std_queue++) {
589                                 hairpin_conf.peers[0].port = port_id;
590                                 hairpin_conf.peers[0].queue =
591                                         std_queue + RXQ_NUM;
592                                 ret = rte_eth_tx_hairpin_queue_setup(
593                                                 port_id, hairpin_q,
594                                                 NR_TXD, &hairpin_conf);
595                                 if (ret != 0)
596                                         rte_exit(EXIT_FAILURE,
597                                                 ":: Hairpin tx queue setup failed: err=%d, port=%u\n",
598                                                 ret, port_id);
599                         }
600                 }
601
602                 ret = rte_eth_dev_start(port_id);
603                 if (ret < 0)
604                         rte_exit(EXIT_FAILURE,
605                                 "rte_eth_dev_start:err=%d, port=%u\n",
606                                 ret, port_id);
607
608                 printf(":: initializing port: %d done\n", port_id);
609         }
610 }
611
612 int
613 main(int argc, char **argv)
614 {
615         int ret;
616         uint16_t port;
617         struct rte_flow_error error;
618
619         ret = rte_eal_init(argc, argv);
620         if (ret < 0)
621                 rte_exit(EXIT_FAILURE, "EAL init failed\n");
622
623         force_quit = false;
624         dump_iterations = false;
625         flows_count = DEFAULT_RULES_COUNT;
626         iterations_number = DEFAULT_ITERATION;
627         flow_group = 0;
628
629         signal(SIGINT, signal_handler);
630         signal(SIGTERM, signal_handler);
631
632         argc -= ret;
633         argv += ret;
634         if (argc > 1)
635                 args_parse(argc, argv);
636
637         init_port();
638
639         nb_lcores = rte_lcore_count();
640         if (nb_lcores <= 1)
641                 rte_exit(EXIT_FAILURE, "This app needs at least two cores\n");
642
643         flows_handler();
644
645         RTE_ETH_FOREACH_DEV(port) {
646                 rte_flow_flush(port, &error);
647                 rte_eth_dev_stop(port);
648                 rte_eth_dev_close(port);
649         }
650         return 0;
651 }