0ff8080aa0448729d6a66613eddce9e059cf346e
[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 #include <unistd.h>
31
32 #include <rte_malloc.h>
33 #include <rte_mempool.h>
34 #include <rte_mbuf.h>
35 #include <rte_ethdev.h>
36 #include <rte_flow.h>
37
38 #include "config.h"
39 #include "flow_gen.h"
40
41 #define MAX_ITERATIONS             100
42 #define DEFAULT_RULES_COUNT    4000000
43 #define DEFAULT_ITERATION       100000
44
45 struct rte_flow *flow;
46 static uint8_t flow_group;
47
48 static uint64_t flow_items[MAX_ITEMS_NUM];
49 static uint64_t flow_actions[MAX_ACTIONS_NUM];
50 static uint64_t flow_attrs[MAX_ATTRS_NUM];
51 static uint8_t items_idx, actions_idx, attrs_idx;
52
53 static volatile bool force_quit;
54 static bool dump_iterations;
55 static bool delete_flag;
56 static bool dump_socket_mem_flag;
57 static bool enable_fwd;
58
59 static struct rte_mempool *mbuf_mp;
60 static uint32_t nb_lcores;
61 static uint32_t flows_count;
62 static uint32_t iterations_number;
63 static uint32_t hairpin_queues_num; /* total hairpin q number - default: 0 */
64 static uint32_t nb_lcores;
65
66 #define MAX_PKT_BURST    32
67 #define LCORE_MODE_PKT    1
68 #define LCORE_MODE_STATS  2
69 #define MAX_STREAMS      64
70 #define MAX_LCORES       64
71
72 struct stream {
73         int tx_port;
74         int tx_queue;
75         int rx_port;
76         int rx_queue;
77 };
78
79 struct lcore_info {
80         int mode;
81         int streams_nb;
82         struct stream streams[MAX_STREAMS];
83         /* stats */
84         uint64_t tx_pkts;
85         uint64_t tx_drops;
86         uint64_t rx_pkts;
87         struct rte_mbuf *pkts[MAX_PKT_BURST];
88 } __rte_cache_aligned;
89
90 static struct lcore_info lcore_infos[MAX_LCORES];
91
92 static void
93 usage(char *progname)
94 {
95         printf("\nusage: %s\n", progname);
96         printf("\nControl configurations:\n");
97         printf("  --flows-count=N: to set the number of needed"
98                 " flows to insert, default is 4,000,000\n");
99         printf("  --dump-iterations: To print rates for each"
100                 " iteration\n");
101         printf("  --deletion-rate: Enable deletion rate"
102                 " calculations\n");
103         printf("  --dump-socket-mem: To dump all socket memory\n");
104         printf("  --enable-fwd: To enable packets forwarding"
105                 " after insertion\n");
106
107         printf("To set flow attributes:\n");
108         printf("  --ingress: set ingress attribute in flows\n");
109         printf("  --egress: set egress attribute in flows\n");
110         printf("  --transfer: set transfer attribute in flows\n");
111         printf("  --group=N: set group for all flows,"
112                 " default is 0\n");
113
114         printf("To set flow items:\n");
115         printf("  --ether: add ether layer in flow items\n");
116         printf("  --vlan: add vlan layer in flow items\n");
117         printf("  --ipv4: add ipv4 layer in flow items\n");
118         printf("  --ipv6: add ipv6 layer in flow items\n");
119         printf("  --tcp: add tcp layer in flow items\n");
120         printf("  --udp: add udp layer in flow items\n");
121         printf("  --vxlan: add vxlan layer in flow items\n");
122         printf("  --vxlan-gpe: add vxlan-gpe layer in flow items\n");
123         printf("  --gre: add gre layer in flow items\n");
124         printf("  --geneve: add geneve layer in flow items\n");
125         printf("  --gtp: add gtp layer in flow items\n");
126         printf("  --meta: add meta layer in flow items\n");
127         printf("  --tag: add tag layer in flow items\n");
128
129         printf("To set flow actions:\n");
130         printf("  --port-id: add port-id action in flow actions\n");
131         printf("  --rss: add rss action in flow actions\n");
132         printf("  --queue: add queue action in flow actions\n");
133         printf("  --jump: add jump action in flow actions\n");
134         printf("  --mark: add mark action in flow actions\n");
135         printf("  --count: add count action in flow actions\n");
136         printf("  --set-meta: add set meta action in flow actions\n");
137         printf("  --set-tag: add set tag action in flow actions\n");
138         printf("  --drop: add drop action in flow actions\n");
139         printf("  --hairpin-queue=N: add hairpin-queue action in flow actions\n");
140         printf("  --hairpin-rss=N: add hairpin-rss action in flow actions\n");
141 }
142
143 static void
144 args_parse(int argc, char **argv)
145 {
146         char **argvopt;
147         int n, opt;
148         int opt_idx;
149         size_t i;
150
151         static const struct option_dict {
152                 const char *str;
153                 const uint64_t mask;
154                 uint64_t *map;
155                 uint8_t *map_idx;
156
157         } flow_options[] = {
158                 {
159                         .str = "ether",
160                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_ETH),
161                         .map = &flow_items[0],
162                         .map_idx = &items_idx
163                 },
164                 {
165                         .str = "ipv4",
166                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_IPV4),
167                         .map = &flow_items[0],
168                         .map_idx = &items_idx
169                 },
170                 {
171                         .str = "ipv6",
172                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_IPV6),
173                         .map = &flow_items[0],
174                         .map_idx = &items_idx
175                 },
176                 {
177                         .str = "vlan",
178                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_VLAN),
179                         .map = &flow_items[0],
180                         .map_idx = &items_idx
181                 },
182                 {
183                         .str = "tcp",
184                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_TCP),
185                         .map = &flow_items[0],
186                         .map_idx = &items_idx
187                 },
188                 {
189                         .str = "udp",
190                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_UDP),
191                         .map = &flow_items[0],
192                         .map_idx = &items_idx
193                 },
194                 {
195                         .str = "vxlan",
196                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_VXLAN),
197                         .map = &flow_items[0],
198                         .map_idx = &items_idx
199                 },
200                 {
201                         .str = "vxlan-gpe",
202                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_VXLAN_GPE),
203                         .map = &flow_items[0],
204                         .map_idx = &items_idx
205                 },
206                 {
207                         .str = "gre",
208                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_GRE),
209                         .map = &flow_items[0],
210                         .map_idx = &items_idx
211                 },
212                 {
213                         .str = "geneve",
214                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_GENEVE),
215                         .map = &flow_items[0],
216                         .map_idx = &items_idx
217                 },
218                 {
219                         .str = "gtp",
220                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_GTP),
221                         .map = &flow_items[0],
222                         .map_idx = &items_idx
223                 },
224                 {
225                         .str = "meta",
226                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_META),
227                         .map = &flow_items[0],
228                         .map_idx = &items_idx
229                 },
230                 {
231                         .str = "tag",
232                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_TAG),
233                         .map = &flow_items[0],
234                         .map_idx = &items_idx
235                 },
236                 {
237                         .str = "ingress",
238                         .mask = INGRESS,
239                         .map = &flow_attrs[0],
240                         .map_idx = &attrs_idx
241                 },
242                 {
243                         .str = "egress",
244                         .mask = EGRESS,
245                         .map = &flow_attrs[0],
246                         .map_idx = &attrs_idx
247                 },
248                 {
249                         .str = "transfer",
250                         .mask = TRANSFER,
251                         .map = &flow_attrs[0],
252                         .map_idx = &attrs_idx
253                 },
254                 {
255                         .str = "port-id",
256                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_PORT_ID),
257                         .map = &flow_actions[0],
258                         .map_idx = &actions_idx
259                 },
260                 {
261                         .str = "rss",
262                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_RSS),
263                         .map = &flow_actions[0],
264                         .map_idx = &actions_idx
265                 },
266                 {
267                         .str = "queue",
268                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_QUEUE),
269                         .map = &flow_actions[0],
270                         .map_idx = &actions_idx
271                 },
272                 {
273                         .str = "jump",
274                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_JUMP),
275                         .map = &flow_actions[0],
276                         .map_idx = &actions_idx
277                 },
278                 {
279                         .str = "mark",
280                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_MARK),
281                         .map = &flow_actions[0],
282                         .map_idx = &actions_idx
283                 },
284                 {
285                         .str = "count",
286                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_COUNT),
287                         .map = &flow_actions[0],
288                         .map_idx = &actions_idx
289                 },
290                 {
291                         .str = "set-meta",
292                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_SET_META),
293                         .map = &flow_actions[0],
294                         .map_idx = &actions_idx
295                 },
296                 {
297                         .str = "set-tag",
298                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_SET_TAG),
299                         .map = &flow_actions[0],
300                         .map_idx = &actions_idx
301                 },
302                 {
303                         .str = "drop",
304                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_DROP),
305                         .map = &flow_actions[0],
306                         .map_idx = &actions_idx
307                 }
308         };
309
310         static const struct option lgopts[] = {
311                 /* Control */
312                 { "help",                       0, 0, 0 },
313                 { "flows-count",                1, 0, 0 },
314                 { "dump-iterations",            0, 0, 0 },
315                 { "deletion-rate",              0, 0, 0 },
316                 { "dump-socket-mem",            0, 0, 0 },
317                 { "enable-fwd",                 0, 0, 0 },
318                 /* Attributes */
319                 { "ingress",                    0, 0, 0 },
320                 { "egress",                     0, 0, 0 },
321                 { "transfer",                   0, 0, 0 },
322                 { "group",                      1, 0, 0 },
323                 /* Items */
324                 { "ether",                      0, 0, 0 },
325                 { "vlan",                       0, 0, 0 },
326                 { "ipv4",                       0, 0, 0 },
327                 { "ipv6",                       0, 0, 0 },
328                 { "tcp",                        0, 0, 0 },
329                 { "udp",                        0, 0, 0 },
330                 { "vxlan",                      0, 0, 0 },
331                 { "vxlan-gpe",                  0, 0, 0 },
332                 { "gre",                        0, 0, 0 },
333                 { "geneve",                     0, 0, 0 },
334                 { "gtp",                        0, 0, 0 },
335                 { "meta",                       0, 0, 0 },
336                 { "tag",                        0, 0, 0 },
337                 /* Actions */
338                 { "port-id",                    0, 0, 0 },
339                 { "rss",                        0, 0, 0 },
340                 { "queue",                      0, 0, 0 },
341                 { "jump",                       0, 0, 0 },
342                 { "mark",                       0, 0, 0 },
343                 { "count",                      0, 0, 0 },
344                 { "set-meta",                   0, 0, 0 },
345                 { "set-tag",                    0, 0, 0 },
346                 { "drop",                       0, 0, 0 },
347                 { "hairpin-queue",              1, 0, 0 },
348                 { "hairpin-rss",                1, 0, 0 },
349         };
350
351         hairpin_queues_num = 0;
352         argvopt = argv;
353
354         printf(":: Flow -> ");
355         while ((opt = getopt_long(argc, argvopt, "",
356                                 lgopts, &opt_idx)) != EOF) {
357                 switch (opt) {
358                 case 0:
359                         if (strcmp(lgopts[opt_idx].name, "help") == 0) {
360                                 usage(argv[0]);
361                                 rte_exit(EXIT_SUCCESS, "Displayed help\n");
362                         }
363
364                         if (strcmp(lgopts[opt_idx].name, "group") == 0) {
365                                 n = atoi(optarg);
366                                 if (n >= 0)
367                                         flow_group = n;
368                                 else
369                                         rte_exit(EXIT_SUCCESS,
370                                                 "flow group should be >= 0\n");
371                                 printf("group %d ", flow_group);
372                         }
373
374                         for (i = 0; i < RTE_DIM(flow_options); i++)
375                                 if (strcmp(lgopts[opt_idx].name,
376                                                 flow_options[i].str) == 0) {
377                                         flow_options[i].map[
378                                         (*flow_options[i].map_idx)++] =
379                                                 flow_options[i].mask;
380                                         printf("%s / ", flow_options[i].str);
381                                 }
382
383                         if (strcmp(lgopts[opt_idx].name,
384                                         "hairpin-rss") == 0) {
385                                 n = atoi(optarg);
386                                 if (n > 0)
387                                         hairpin_queues_num = n;
388                                 else
389                                         rte_exit(EXIT_SUCCESS,
390                                                 "Hairpin queues should be > 0\n");
391
392                                 flow_actions[actions_idx++] =
393                                         HAIRPIN_RSS_ACTION;
394                                 printf("hairpin-rss / ");
395                         }
396                         if (strcmp(lgopts[opt_idx].name,
397                                         "hairpin-queue") == 0) {
398                                 n = atoi(optarg);
399                                 if (n > 0)
400                                         hairpin_queues_num = n;
401                                 else
402                                         rte_exit(EXIT_SUCCESS,
403                                                 "Hairpin queues should be > 0\n");
404
405                                 flow_actions[actions_idx++] =
406                                         HAIRPIN_QUEUE_ACTION;
407                                 printf("hairpin-queue / ");
408                         }
409
410                         /* Control */
411                         if (strcmp(lgopts[opt_idx].name,
412                                         "flows-count") == 0) {
413                                 n = atoi(optarg);
414                                 if (n > (int) iterations_number)
415                                         flows_count = n;
416                                 else {
417                                         printf("\n\nflows_count should be > %d\n",
418                                                 iterations_number);
419                                         rte_exit(EXIT_SUCCESS, " ");
420                                 }
421                         }
422                         if (strcmp(lgopts[opt_idx].name,
423                                         "dump-iterations") == 0)
424                                 dump_iterations = true;
425                         if (strcmp(lgopts[opt_idx].name,
426                                         "deletion-rate") == 0)
427                                 delete_flag = true;
428                         if (strcmp(lgopts[opt_idx].name,
429                                         "dump-socket-mem") == 0)
430                                 dump_socket_mem_flag = true;
431                         if (strcmp(lgopts[opt_idx].name,
432                                         "enable-fwd") == 0)
433                                 enable_fwd = true;
434                         break;
435                 default:
436                         fprintf(stderr, "Invalid option: %s\n", argv[optind]);
437                         usage(argv[0]);
438                         rte_exit(EXIT_SUCCESS, "Invalid option\n");
439                         break;
440                 }
441         }
442         printf("end_flow\n");
443 }
444
445 /* Dump the socket memory statistics on console */
446 static size_t
447 dump_socket_mem(FILE *f)
448 {
449         struct rte_malloc_socket_stats socket_stats;
450         unsigned int i = 0;
451         size_t total = 0;
452         size_t alloc = 0;
453         size_t free = 0;
454         unsigned int n_alloc = 0;
455         unsigned int n_free = 0;
456         bool active_nodes = false;
457
458
459         for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
460                 if (rte_malloc_get_socket_stats(i, &socket_stats) ||
461                     !socket_stats.heap_totalsz_bytes)
462                         continue;
463                 active_nodes = true;
464                 total += socket_stats.heap_totalsz_bytes;
465                 alloc += socket_stats.heap_allocsz_bytes;
466                 free += socket_stats.heap_freesz_bytes;
467                 n_alloc += socket_stats.alloc_count;
468                 n_free += socket_stats.free_count;
469                 if (dump_socket_mem_flag) {
470                         fprintf(f, "::::::::::::::::::::::::::::::::::::::::");
471                         fprintf(f,
472                                 "\nSocket %u:\nsize(M) total: %.6lf\nalloc:"
473                                 " %.6lf(%.3lf%%)\nfree: %.6lf"
474                                 "\nmax: %.6lf"
475                                 "\ncount alloc: %u\nfree: %u\n",
476                                 i,
477                                 socket_stats.heap_totalsz_bytes / 1.0e6,
478                                 socket_stats.heap_allocsz_bytes / 1.0e6,
479                                 (double)socket_stats.heap_allocsz_bytes * 100 /
480                                 (double)socket_stats.heap_totalsz_bytes,
481                                 socket_stats.heap_freesz_bytes / 1.0e6,
482                                 socket_stats.greatest_free_size / 1.0e6,
483                                 socket_stats.alloc_count,
484                                 socket_stats.free_count);
485                                 fprintf(f, "::::::::::::::::::::::::::::::::::::::::");
486                 }
487         }
488         if (dump_socket_mem_flag && active_nodes) {
489                 fprintf(f,
490                         "\nTotal: size(M)\ntotal: %.6lf"
491                         "\nalloc: %.6lf(%.3lf%%)\nfree: %.6lf"
492                         "\ncount alloc: %u\nfree: %u\n",
493                         total / 1.0e6, alloc / 1.0e6,
494                         (double)alloc * 100 / (double)total, free / 1.0e6,
495                         n_alloc, n_free);
496                 fprintf(f, "::::::::::::::::::::::::::::::::::::::::\n");
497         }
498         return alloc;
499 }
500
501 static void
502 print_flow_error(struct rte_flow_error error)
503 {
504         printf("Flow can't be created %d message: %s\n",
505                 error.type,
506                 error.message ? error.message : "(no stated reason)");
507 }
508
509 static inline void
510 destroy_flows(int port_id, struct rte_flow **flow_list)
511 {
512         struct rte_flow_error error;
513         clock_t start_iter, end_iter;
514         double cpu_time_used = 0;
515         double flows_rate;
516         double cpu_time_per_iter[MAX_ITERATIONS];
517         double delta;
518         uint32_t i;
519         int iter_id;
520
521         for (i = 0; i < MAX_ITERATIONS; i++)
522                 cpu_time_per_iter[i] = -1;
523
524         if (iterations_number > flows_count)
525                 iterations_number = flows_count;
526
527         /* Deletion Rate */
528         printf("Flows Deletion on port = %d\n", port_id);
529         start_iter = clock();
530         for (i = 0; i < flows_count; i++) {
531                 if (flow_list[i] == 0)
532                         break;
533
534                 memset(&error, 0x33, sizeof(error));
535                 if (rte_flow_destroy(port_id, flow_list[i], &error)) {
536                         print_flow_error(error);
537                         rte_exit(EXIT_FAILURE, "Error in deleting flow");
538                 }
539
540                 if (i && !((i + 1) % iterations_number)) {
541                         /* Save the deletion rate of each iter */
542                         end_iter = clock();
543                         delta = (double) (end_iter - start_iter);
544                         iter_id = ((i + 1) / iterations_number) - 1;
545                         cpu_time_per_iter[iter_id] =
546                                 delta / CLOCKS_PER_SEC;
547                         cpu_time_used += cpu_time_per_iter[iter_id];
548                         start_iter = clock();
549                 }
550         }
551
552         /* Deletion rate per iteration */
553         if (dump_iterations)
554                 for (i = 0; i < MAX_ITERATIONS; i++) {
555                         if (cpu_time_per_iter[i] == -1)
556                                 continue;
557                         delta = (double)(iterations_number /
558                                 cpu_time_per_iter[i]);
559                         flows_rate = delta / 1000;
560                         printf(":: Iteration #%d: %d flows "
561                                 "in %f sec[ Rate = %f K/Sec ]\n",
562                                 i, iterations_number,
563                                 cpu_time_per_iter[i], flows_rate);
564                 }
565
566         /* Deletion rate for all flows */
567         flows_rate = ((double) (flows_count / cpu_time_used) / 1000);
568         printf("\n:: Total flow deletion rate -> %f K/Sec\n",
569                 flows_rate);
570         printf(":: The time for deleting %d in flows %f seconds\n",
571                 flows_count, cpu_time_used);
572 }
573
574 static inline void
575 flows_handler(void)
576 {
577         struct rte_flow **flow_list;
578         struct rte_flow_error error;
579         clock_t start_iter, end_iter;
580         double cpu_time_used;
581         double flows_rate;
582         double cpu_time_per_iter[MAX_ITERATIONS];
583         double delta;
584         uint16_t nr_ports;
585         uint32_t i;
586         int port_id;
587         int iter_id;
588         uint32_t flow_index;
589         uint64_t global_items[MAX_ITEMS_NUM] = { 0 };
590         uint64_t global_actions[MAX_ACTIONS_NUM] = { 0 };
591
592         global_items[0] = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_ETH);
593         global_actions[0] = FLOW_ITEM_MASK(RTE_FLOW_ACTION_TYPE_JUMP);
594
595         nr_ports = rte_eth_dev_count_avail();
596
597         for (i = 0; i < MAX_ITERATIONS; i++)
598                 cpu_time_per_iter[i] = -1;
599
600         if (iterations_number > flows_count)
601                 iterations_number = flows_count;
602
603         printf(":: Flows Count per port: %d\n", flows_count);
604
605         flow_list = rte_zmalloc("flow_list",
606                 (sizeof(struct rte_flow *) * flows_count) + 1, 0);
607         if (flow_list == NULL)
608                 rte_exit(EXIT_FAILURE, "No Memory available!");
609
610         for (port_id = 0; port_id < nr_ports; port_id++) {
611                 cpu_time_used = 0;
612                 flow_index = 0;
613                 if (flow_group > 0) {
614                         /*
615                          * Create global rule to jump into flow_group,
616                          * this way the app will avoid the default rules.
617                          *
618                          * Global rule:
619                          * group 0 eth / end actions jump group <flow_group>
620                          *
621                          */
622                         flow = generate_flow(port_id, 0, flow_attrs,
623                                 global_items, global_actions,
624                                 flow_group, 0, 0, &error);
625
626                         if (flow == NULL) {
627                                 print_flow_error(error);
628                                 rte_exit(EXIT_FAILURE, "error in creating flow");
629                         }
630                         flow_list[flow_index++] = flow;
631                 }
632
633                 /* Insertion Rate */
634                 printf("Flows insertion on port = %d\n", port_id);
635                 start_iter = clock();
636                 for (i = 0; i < flows_count; i++) {
637                         flow = generate_flow(port_id, flow_group,
638                                 flow_attrs, flow_items, flow_actions,
639                                 JUMP_ACTION_TABLE, i,
640                                 hairpin_queues_num, &error);
641
642                         if (force_quit)
643                                 i = flows_count;
644
645                         if (!flow) {
646                                 print_flow_error(error);
647                                 rte_exit(EXIT_FAILURE, "error in creating flow");
648                         }
649
650                         flow_list[flow_index++] = flow;
651
652                         if (i && !((i + 1) % iterations_number)) {
653                                 /* Save the insertion rate of each iter */
654                                 end_iter = clock();
655                                 delta = (double) (end_iter - start_iter);
656                                 iter_id = ((i + 1) / iterations_number) - 1;
657                                 cpu_time_per_iter[iter_id] =
658                                         delta / CLOCKS_PER_SEC;
659                                 cpu_time_used += cpu_time_per_iter[iter_id];
660                                 start_iter = clock();
661                         }
662                 }
663
664                 /* Iteration rate per iteration */
665                 if (dump_iterations)
666                         for (i = 0; i < MAX_ITERATIONS; i++) {
667                                 if (cpu_time_per_iter[i] == -1)
668                                         continue;
669                                 delta = (double)(iterations_number /
670                                         cpu_time_per_iter[i]);
671                                 flows_rate = delta / 1000;
672                                 printf(":: Iteration #%d: %d flows "
673                                         "in %f sec[ Rate = %f K/Sec ]\n",
674                                         i, iterations_number,
675                                         cpu_time_per_iter[i], flows_rate);
676                         }
677
678                 /* Insertion rate for all flows */
679                 flows_rate = ((double) (flows_count / cpu_time_used) / 1000);
680                 printf("\n:: Total flow insertion rate -> %f K/Sec\n",
681                                                 flows_rate);
682                 printf(":: The time for creating %d in flows %f seconds\n",
683                                                 flows_count, cpu_time_used);
684
685                 if (delete_flag)
686                         destroy_flows(port_id, flow_list);
687         }
688 }
689
690 static void
691 signal_handler(int signum)
692 {
693         if (signum == SIGINT || signum == SIGTERM) {
694                 printf("\n\nSignal %d received, preparing to exit...\n",
695                                         signum);
696                 printf("Error: Stats are wrong due to sudden signal!\n\n");
697                 force_quit = true;
698         }
699 }
700
701 static inline uint16_t
702 do_rx(struct lcore_info *li, uint16_t rx_port, uint16_t rx_queue)
703 {
704         uint16_t cnt = 0;
705         cnt = rte_eth_rx_burst(rx_port, rx_queue, li->pkts, MAX_PKT_BURST);
706         li->rx_pkts += cnt;
707         return cnt;
708 }
709
710 static inline void
711 do_tx(struct lcore_info *li, uint16_t cnt, uint16_t tx_port,
712                         uint16_t tx_queue)
713 {
714         uint16_t nr_tx = 0;
715         uint16_t i;
716
717         nr_tx = rte_eth_tx_burst(tx_port, tx_queue, li->pkts, cnt);
718         li->tx_pkts  += nr_tx;
719         li->tx_drops += cnt - nr_tx;
720
721         for (i = nr_tx; i < cnt; i++)
722                 rte_pktmbuf_free(li->pkts[i]);
723 }
724
725 /*
726  * Method to convert numbers into pretty numbers that easy
727  * to read. The design here is to add comma after each three
728  * digits and set all of this inside buffer.
729  *
730  * For example if n = 1799321, the output will be
731  * 1,799,321 after this method which is easier to read.
732  */
733 static char *
734 pretty_number(uint64_t n, char *buf)
735 {
736         char p[6][4];
737         int i = 0;
738         int off = 0;
739
740         while (n > 1000) {
741                 sprintf(p[i], "%03d", (int)(n % 1000));
742                 n /= 1000;
743                 i += 1;
744         }
745
746         sprintf(p[i++], "%d", (int)n);
747
748         while (i--)
749                 off += sprintf(buf + off, "%s,", p[i]);
750         buf[strlen(buf) - 1] = '\0';
751
752         return buf;
753 }
754
755 static void
756 packet_per_second_stats(void)
757 {
758         struct lcore_info *old;
759         struct lcore_info *li, *oli;
760         int nr_lines = 0;
761         int i;
762
763         old = rte_zmalloc("old",
764                 sizeof(struct lcore_info) * MAX_LCORES, 0);
765         if (old == NULL)
766                 rte_exit(EXIT_FAILURE, "No Memory available!");
767
768         memcpy(old, lcore_infos,
769                 sizeof(struct lcore_info) * MAX_LCORES);
770
771         while (!force_quit) {
772                 uint64_t total_tx_pkts = 0;
773                 uint64_t total_rx_pkts = 0;
774                 uint64_t total_tx_drops = 0;
775                 uint64_t tx_delta, rx_delta, drops_delta;
776                 char buf[3][32];
777                 int nr_valid_core = 0;
778
779                 sleep(1);
780
781                 if (nr_lines) {
782                         char go_up_nr_lines[16];
783
784                         sprintf(go_up_nr_lines, "%c[%dA\r", 27, nr_lines);
785                         printf("%s\r", go_up_nr_lines);
786                 }
787
788                 printf("\n%6s %16s %16s %16s\n", "core", "tx", "tx drops", "rx");
789                 printf("%6s %16s %16s %16s\n", "------", "----------------",
790                         "----------------", "----------------");
791                 nr_lines = 3;
792                 for (i = 0; i < MAX_LCORES; i++) {
793                         li  = &lcore_infos[i];
794                         oli = &old[i];
795                         if (li->mode != LCORE_MODE_PKT)
796                                 continue;
797
798                         tx_delta    = li->tx_pkts  - oli->tx_pkts;
799                         rx_delta    = li->rx_pkts  - oli->rx_pkts;
800                         drops_delta = li->tx_drops - oli->tx_drops;
801                         printf("%6d %16s %16s %16s\n", i,
802                                 pretty_number(tx_delta,    buf[0]),
803                                 pretty_number(drops_delta, buf[1]),
804                                 pretty_number(rx_delta,    buf[2]));
805
806                         total_tx_pkts  += tx_delta;
807                         total_rx_pkts  += rx_delta;
808                         total_tx_drops += drops_delta;
809
810                         nr_valid_core++;
811                         nr_lines += 1;
812                 }
813
814                 if (nr_valid_core > 1) {
815                         printf("%6s %16s %16s %16s\n", "total",
816                                 pretty_number(total_tx_pkts,  buf[0]),
817                                 pretty_number(total_tx_drops, buf[1]),
818                                 pretty_number(total_rx_pkts,  buf[2]));
819                         nr_lines += 1;
820                 }
821
822                 memcpy(old, lcore_infos,
823                         sizeof(struct lcore_info) * MAX_LCORES);
824         }
825 }
826
827 static int
828 start_forwarding(void *data __rte_unused)
829 {
830         int lcore = rte_lcore_id();
831         int stream_id;
832         uint16_t cnt;
833         struct lcore_info *li = &lcore_infos[lcore];
834
835         if (!li->mode)
836                 return 0;
837
838         if (li->mode == LCORE_MODE_STATS) {
839                 printf(":: started stats on lcore %u\n", lcore);
840                 packet_per_second_stats();
841                 return 0;
842         }
843
844         while (!force_quit)
845                 for (stream_id = 0; stream_id < MAX_STREAMS; stream_id++) {
846                         if (li->streams[stream_id].rx_port == -1)
847                                 continue;
848
849                         cnt = do_rx(li,
850                                         li->streams[stream_id].rx_port,
851                                         li->streams[stream_id].rx_queue);
852                         if (cnt)
853                                 do_tx(li, cnt,
854                                         li->streams[stream_id].tx_port,
855                                         li->streams[stream_id].tx_queue);
856                 }
857         return 0;
858 }
859
860 static void
861 init_lcore_info(void)
862 {
863         int i, j;
864         unsigned int lcore;
865         uint16_t nr_port;
866         uint16_t queue;
867         int port;
868         int stream_id = 0;
869         int streams_per_core;
870         int unassigned_streams;
871         int nb_fwd_streams;
872         nr_port = rte_eth_dev_count_avail();
873
874         /* First logical core is reserved for stats printing */
875         lcore = rte_get_next_lcore(-1, 0, 0);
876         lcore_infos[lcore].mode = LCORE_MODE_STATS;
877
878         /*
879          * Initialize all cores
880          * All cores at first must have -1 value in all streams
881          * This means that this stream is not used, or not set
882          * yet.
883          */
884         for (i = 0; i < MAX_LCORES; i++)
885                 for (j = 0; j < MAX_STREAMS; j++) {
886                         lcore_infos[i].streams[j].tx_port = -1;
887                         lcore_infos[i].streams[j].rx_port = -1;
888                         lcore_infos[i].streams[j].tx_queue = -1;
889                         lcore_infos[i].streams[j].rx_queue = -1;
890                         lcore_infos[i].streams_nb = 0;
891                 }
892
893         /*
894          * Calculate the total streams count.
895          * Also distribute those streams count between the available
896          * logical cores except first core, since it's reserved for
897          * stats prints.
898          */
899         nb_fwd_streams = nr_port * RXQ_NUM;
900         if ((int)(nb_lcores - 1) >= nb_fwd_streams)
901                 for (i = 0; i < (int)(nb_lcores - 1); i++) {
902                         lcore = rte_get_next_lcore(lcore, 0, 0);
903                         lcore_infos[lcore].streams_nb = 1;
904                 }
905         else {
906                 streams_per_core = nb_fwd_streams / (nb_lcores - 1);
907                 unassigned_streams = nb_fwd_streams % (nb_lcores - 1);
908                 for (i = 0; i < (int)(nb_lcores - 1); i++) {
909                         lcore = rte_get_next_lcore(lcore, 0, 0);
910                         lcore_infos[lcore].streams_nb = streams_per_core;
911                         if (unassigned_streams) {
912                                 lcore_infos[lcore].streams_nb++;
913                                 unassigned_streams--;
914                         }
915                 }
916         }
917
918         /*
919          * Set the streams for the cores according to each logical
920          * core stream count.
921          * The streams is built on the design of what received should
922          * forward as well, this means that if you received packets on
923          * port 0 queue 0 then the same queue should forward the
924          * packets, using the same logical core.
925          */
926         lcore = rte_get_next_lcore(-1, 0, 0);
927         for (port = 0; port < nr_port; port++) {
928                 /* Create FWD stream */
929                 for (queue = 0; queue < RXQ_NUM; queue++) {
930                         if (!lcore_infos[lcore].streams_nb ||
931                                 !(stream_id % lcore_infos[lcore].streams_nb)) {
932                                 lcore = rte_get_next_lcore(lcore, 0, 0);
933                                 lcore_infos[lcore].mode = LCORE_MODE_PKT;
934                                 stream_id = 0;
935                         }
936                         lcore_infos[lcore].streams[stream_id].rx_queue = queue;
937                         lcore_infos[lcore].streams[stream_id].tx_queue = queue;
938                         lcore_infos[lcore].streams[stream_id].rx_port = port;
939                         lcore_infos[lcore].streams[stream_id].tx_port = port;
940                         stream_id++;
941                 }
942         }
943
944         /* Print all streams */
945         printf(":: Stream -> core id[N]: (rx_port, rx_queue)->(tx_port, tx_queue)\n");
946         for (i = 0; i < MAX_LCORES; i++)
947                 for (j = 0; j < MAX_STREAMS; j++) {
948                         /* No streams for this core */
949                         if (lcore_infos[i].streams[j].tx_port == -1)
950                                 break;
951                         printf("Stream -> core id[%d]: (%d,%d)->(%d,%d)\n",
952                                 i,
953                                 lcore_infos[i].streams[j].rx_port,
954                                 lcore_infos[i].streams[j].rx_queue,
955                                 lcore_infos[i].streams[j].tx_port,
956                                 lcore_infos[i].streams[j].tx_queue);
957                 }
958 }
959
960 static void
961 init_port(void)
962 {
963         int ret;
964         uint16_t std_queue;
965         uint16_t hairpin_queue;
966         uint16_t port_id;
967         uint16_t nr_ports;
968         uint16_t nr_queues;
969         struct rte_eth_hairpin_conf hairpin_conf = {
970                 .peer_count = 1,
971         };
972         struct rte_eth_conf port_conf = {
973                 .rx_adv_conf = {
974                         .rss_conf.rss_hf =
975                                 GET_RSS_HF(),
976                 }
977         };
978         struct rte_eth_txconf txq_conf;
979         struct rte_eth_rxconf rxq_conf;
980         struct rte_eth_dev_info dev_info;
981
982         nr_queues = RXQ_NUM;
983         if (hairpin_queues_num != 0)
984                 nr_queues = RXQ_NUM + hairpin_queues_num;
985
986         nr_ports = rte_eth_dev_count_avail();
987         if (nr_ports == 0)
988                 rte_exit(EXIT_FAILURE, "Error: no port detected\n");
989
990         mbuf_mp = rte_pktmbuf_pool_create("mbuf_pool",
991                                         TOTAL_MBUF_NUM, MBUF_CACHE_SIZE,
992                                         0, MBUF_SIZE,
993                                         rte_socket_id());
994         if (mbuf_mp == NULL)
995                 rte_exit(EXIT_FAILURE, "Error: can't init mbuf pool\n");
996
997         for (port_id = 0; port_id < nr_ports; port_id++) {
998                 ret = rte_eth_dev_info_get(port_id, &dev_info);
999                 if (ret != 0)
1000                         rte_exit(EXIT_FAILURE,
1001                                 "Error during getting device"
1002                                 " (port %u) info: %s\n",
1003                                 port_id, strerror(-ret));
1004
1005                 port_conf.txmode.offloads &= dev_info.tx_offload_capa;
1006                 port_conf.rxmode.offloads &= dev_info.rx_offload_capa;
1007
1008                 printf(":: initializing port: %d\n", port_id);
1009
1010                 ret = rte_eth_dev_configure(port_id, nr_queues,
1011                                 nr_queues, &port_conf);
1012                 if (ret < 0)
1013                         rte_exit(EXIT_FAILURE,
1014                                 ":: cannot configure device: err=%d, port=%u\n",
1015                                 ret, port_id);
1016
1017                 rxq_conf = dev_info.default_rxconf;
1018                 for (std_queue = 0; std_queue < RXQ_NUM; std_queue++) {
1019                         ret = rte_eth_rx_queue_setup(port_id, std_queue, NR_RXD,
1020                                         rte_eth_dev_socket_id(port_id),
1021                                         &rxq_conf,
1022                                         mbuf_mp);
1023                         if (ret < 0)
1024                                 rte_exit(EXIT_FAILURE,
1025                                         ":: Rx queue setup failed: err=%d, port=%u\n",
1026                                         ret, port_id);
1027                 }
1028
1029                 txq_conf = dev_info.default_txconf;
1030                 for (std_queue = 0; std_queue < TXQ_NUM; std_queue++) {
1031                         ret = rte_eth_tx_queue_setup(port_id, std_queue, NR_TXD,
1032                                         rte_eth_dev_socket_id(port_id),
1033                                         &txq_conf);
1034                         if (ret < 0)
1035                                 rte_exit(EXIT_FAILURE,
1036                                         ":: Tx queue setup failed: err=%d, port=%u\n",
1037                                         ret, port_id);
1038                 }
1039
1040                 /* Catch all packets from traffic generator. */
1041                 ret = rte_eth_promiscuous_enable(port_id);
1042                 if (ret != 0)
1043                         rte_exit(EXIT_FAILURE,
1044                                 ":: promiscuous mode enable failed: err=%s, port=%u\n",
1045                                 rte_strerror(-ret), port_id);
1046
1047                 if (hairpin_queues_num != 0) {
1048                         /*
1049                          * Configure peer which represents hairpin Tx.
1050                          * Hairpin queue numbers start after standard queues
1051                          * (RXQ_NUM and TXQ_NUM).
1052                          */
1053                         for (hairpin_queue = RXQ_NUM, std_queue = 0;
1054                                         hairpin_queue < nr_queues;
1055                                         hairpin_queue++, std_queue++) {
1056                                 hairpin_conf.peers[0].port = port_id;
1057                                 hairpin_conf.peers[0].queue =
1058                                         std_queue + TXQ_NUM;
1059                                 ret = rte_eth_rx_hairpin_queue_setup(
1060                                                 port_id, hairpin_queue,
1061                                                 NR_RXD, &hairpin_conf);
1062                                 if (ret != 0)
1063                                         rte_exit(EXIT_FAILURE,
1064                                                 ":: Hairpin rx queue setup failed: err=%d, port=%u\n",
1065                                                 ret, port_id);
1066                         }
1067
1068                         for (hairpin_queue = TXQ_NUM, std_queue = 0;
1069                                         hairpin_queue < nr_queues;
1070                                         hairpin_queue++, std_queue++) {
1071                                 hairpin_conf.peers[0].port = port_id;
1072                                 hairpin_conf.peers[0].queue =
1073                                         std_queue + RXQ_NUM;
1074                                 ret = rte_eth_tx_hairpin_queue_setup(
1075                                                 port_id, hairpin_queue,
1076                                                 NR_TXD, &hairpin_conf);
1077                                 if (ret != 0)
1078                                         rte_exit(EXIT_FAILURE,
1079                                                 ":: Hairpin tx queue setup failed: err=%d, port=%u\n",
1080                                                 ret, port_id);
1081                         }
1082                 }
1083
1084                 ret = rte_eth_dev_start(port_id);
1085                 if (ret < 0)
1086                         rte_exit(EXIT_FAILURE,
1087                                 "rte_eth_dev_start:err=%d, port=%u\n",
1088                                 ret, port_id);
1089
1090                 printf(":: initializing port: %d done\n", port_id);
1091         }
1092 }
1093
1094 int
1095 main(int argc, char **argv)
1096 {
1097         int ret;
1098         uint16_t port;
1099         struct rte_flow_error error;
1100         int64_t alloc, last_alloc;
1101
1102         ret = rte_eal_init(argc, argv);
1103         if (ret < 0)
1104                 rte_exit(EXIT_FAILURE, "EAL init failed\n");
1105
1106         force_quit = false;
1107         dump_iterations = false;
1108         flows_count = DEFAULT_RULES_COUNT;
1109         iterations_number = DEFAULT_ITERATION;
1110         delete_flag = false;
1111         dump_socket_mem_flag = false;
1112         flow_group = 0;
1113
1114         signal(SIGINT, signal_handler);
1115         signal(SIGTERM, signal_handler);
1116
1117         argc -= ret;
1118         argv += ret;
1119         if (argc > 1)
1120                 args_parse(argc, argv);
1121
1122         init_port();
1123
1124         nb_lcores = rte_lcore_count();
1125         if (nb_lcores <= 1)
1126                 rte_exit(EXIT_FAILURE, "This app needs at least two cores\n");
1127
1128         last_alloc = (int64_t)dump_socket_mem(stdout);
1129         flows_handler();
1130         alloc = (int64_t)dump_socket_mem(stdout);
1131
1132         if (last_alloc)
1133                 fprintf(stdout, ":: Memory allocation change(M): %.6lf\n",
1134                 (alloc - last_alloc) / 1.0e6);
1135
1136         if (enable_fwd) {
1137                 init_lcore_info();
1138                 rte_eal_mp_remote_launch(start_forwarding, NULL, CALL_MASTER);
1139         }
1140
1141         RTE_ETH_FOREACH_DEV(port) {
1142                 rte_flow_flush(port, &error);
1143                 rte_eth_dev_stop(port);
1144                 rte_eth_dev_close(port);
1145         }
1146         return 0;
1147 }