d10fa3fe64a67401d2e26c0c11d505eed8bcc60c
[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_BATCHES_COUNT          100
42 #define DEFAULT_RULES_COUNT    4000000
43 #define DEFAULT_RULES_BATCH     100000
44 #define DEFAULT_GROUP                0
45
46 struct rte_flow *flow;
47 static uint8_t flow_group;
48
49 static uint64_t encap_data;
50 static uint64_t decap_data;
51
52 static uint64_t flow_items[MAX_ITEMS_NUM];
53 static uint64_t flow_actions[MAX_ACTIONS_NUM];
54 static uint64_t flow_attrs[MAX_ATTRS_NUM];
55 static uint8_t items_idx, actions_idx, attrs_idx;
56
57 static uint64_t ports_mask;
58 static volatile bool force_quit;
59 static bool dump_iterations;
60 static bool delete_flag;
61 static bool dump_socket_mem_flag;
62 static bool enable_fwd;
63
64 static struct rte_mempool *mbuf_mp;
65 static uint32_t nb_lcores;
66 static uint32_t rules_count;
67 static uint32_t rules_batch;
68 static uint32_t hairpin_queues_num; /* total hairpin q number - default: 0 */
69 static uint32_t nb_lcores;
70
71 #define MAX_PKT_BURST    32
72 #define LCORE_MODE_PKT    1
73 #define LCORE_MODE_STATS  2
74 #define MAX_STREAMS      64
75
76 struct stream {
77         int tx_port;
78         int tx_queue;
79         int rx_port;
80         int rx_queue;
81 };
82
83 struct lcore_info {
84         int mode;
85         int streams_nb;
86         struct stream streams[MAX_STREAMS];
87         /* stats */
88         uint64_t tx_pkts;
89         uint64_t tx_drops;
90         uint64_t rx_pkts;
91         struct rte_mbuf *pkts[MAX_PKT_BURST];
92 } __rte_cache_aligned;
93
94 static struct lcore_info lcore_infos[RTE_MAX_LCORE];
95
96 struct used_cpu_time {
97         double insertion[MAX_PORTS][RTE_MAX_LCORE];
98         double deletion[MAX_PORTS][RTE_MAX_LCORE];
99 };
100
101 struct multi_cores_pool {
102         uint32_t cores_count;
103         uint32_t rules_count;
104         struct used_cpu_time create_meter;
105         struct used_cpu_time create_flow;
106         int64_t last_alloc[RTE_MAX_LCORE];
107         int64_t current_alloc[RTE_MAX_LCORE];
108 } __rte_cache_aligned;
109
110 static struct multi_cores_pool mc_pool = {
111         .cores_count = 1,
112 };
113
114 static void
115 usage(char *progname)
116 {
117         printf("\nusage: %s\n", progname);
118         printf("\nControl configurations:\n");
119         printf("  --rules-count=N: to set the number of needed"
120                 " rules to insert, default is %d\n", DEFAULT_RULES_COUNT);
121         printf("  --rules-batch=N: set number of batched rules,"
122                 " default is %d\n", DEFAULT_RULES_BATCH);
123         printf("  --dump-iterations: To print rates for each"
124                 " iteration\n");
125         printf("  --deletion-rate: Enable deletion rate"
126                 " calculations\n");
127         printf("  --dump-socket-mem: To dump all socket memory\n");
128         printf("  --enable-fwd: To enable packets forwarding"
129                 " after insertion\n");
130         printf("  --portmask=N: hexadecimal bitmask of ports used\n");
131
132         printf("To set flow attributes:\n");
133         printf("  --ingress: set ingress attribute in flows\n");
134         printf("  --egress: set egress attribute in flows\n");
135         printf("  --transfer: set transfer attribute in flows\n");
136         printf("  --group=N: set group for all flows,"
137                 " default is %d\n", DEFAULT_GROUP);
138         printf("  --cores=N: to set the number of needed "
139                 "cores to insert rte_flow rules, default is 1\n");
140
141         printf("To set flow items:\n");
142         printf("  --ether: add ether layer in flow items\n");
143         printf("  --vlan: add vlan layer in flow items\n");
144         printf("  --ipv4: add ipv4 layer in flow items\n");
145         printf("  --ipv6: add ipv6 layer in flow items\n");
146         printf("  --tcp: add tcp layer in flow items\n");
147         printf("  --udp: add udp layer in flow items\n");
148         printf("  --vxlan: add vxlan layer in flow items\n");
149         printf("  --vxlan-gpe: add vxlan-gpe layer in flow items\n");
150         printf("  --gre: add gre layer in flow items\n");
151         printf("  --geneve: add geneve layer in flow items\n");
152         printf("  --gtp: add gtp layer in flow items\n");
153         printf("  --meta: add meta layer in flow items\n");
154         printf("  --tag: add tag layer in flow items\n");
155         printf("  --icmpv4: add icmpv4 layer in flow items\n");
156         printf("  --icmpv6: add icmpv6 layer in flow items\n");
157
158         printf("To set flow actions:\n");
159         printf("  --port-id: add port-id action in flow actions\n");
160         printf("  --rss: add rss action in flow actions\n");
161         printf("  --queue: add queue action in flow actions\n");
162         printf("  --jump: add jump action in flow actions\n");
163         printf("  --mark: add mark action in flow actions\n");
164         printf("  --count: add count action in flow actions\n");
165         printf("  --set-meta: add set meta action in flow actions\n");
166         printf("  --set-tag: add set tag action in flow actions\n");
167         printf("  --drop: add drop action in flow actions\n");
168         printf("  --hairpin-queue=N: add hairpin-queue action in flow actions\n");
169         printf("  --hairpin-rss=N: add hairpin-rss action in flow actions\n");
170         printf("  --set-src-mac: add set src mac action to flow actions\n"
171                 "Src mac to be set is random each flow\n");
172         printf("  --set-dst-mac: add set dst mac action to flow actions\n"
173                  "Dst mac to be set is random each flow\n");
174         printf("  --set-src-ipv4: add set src ipv4 action to flow actions\n"
175                 "Src ipv4 to be set is random each flow\n");
176         printf("  --set-dst-ipv4 add set dst ipv4 action to flow actions\n"
177                 "Dst ipv4 to be set is random each flow\n");
178         printf("  --set-src-ipv6: add set src ipv6 action to flow actions\n"
179                 "Src ipv6 to be set is random each flow\n");
180         printf("  --set-dst-ipv6: add set dst ipv6 action to flow actions\n"
181                 "Dst ipv6 to be set is random each flow\n");
182         printf("  --set-src-tp: add set src tp action to flow actions\n"
183                 "Src tp to be set is random each flow\n");
184         printf("  --set-dst-tp: add set dst tp action to flow actions\n"
185                 "Dst tp to be set is random each flow\n");
186         printf("  --inc-tcp-ack: add inc tcp ack action to flow actions\n"
187                 "tcp ack will be increments by 1\n");
188         printf("  --dec-tcp-ack: add dec tcp ack action to flow actions\n"
189                 "tcp ack will be decrements by 1\n");
190         printf("  --inc-tcp-seq: add inc tcp seq action to flow actions\n"
191                 "tcp seq will be increments by 1\n");
192         printf("  --dec-tcp-seq: add dec tcp seq action to flow actions\n"
193                 "tcp seq will be decrements by 1\n");
194         printf("  --set-ttl: add set ttl action to flow actions\n"
195                 "L3 ttl to be set is random each flow\n");
196         printf("  --dec-ttl: add dec ttl action to flow actions\n"
197                 "L3 ttl will be decrements by 1\n");
198         printf("  --set-ipv4-dscp: add set ipv4 dscp action to flow actions\n"
199                 "ipv4 dscp value to be set is random each flow\n");
200         printf("  --set-ipv6-dscp: add set ipv6 dscp action to flow actions\n"
201                 "ipv6 dscp value to be set is random each flow\n");
202         printf("  --flag: add flag action to flow actions\n");
203         printf("  --raw-encap=<data>: add raw encap action to flow actions\n"
204                 "Data is the data needed to be encaped\n"
205                 "Example: raw-encap=ether,ipv4,udp,vxlan\n");
206         printf("  --raw-decap=<data>: add raw decap action to flow actions\n"
207                 "Data is the data needed to be decaped\n"
208                 "Example: raw-decap=ether,ipv4,udp,vxlan\n");
209         printf("  --vxlan-encap: add vxlan-encap action to flow actions\n"
210                 "Encapped data is fixed with pattern: ether,ipv4,udp,vxlan\n"
211                 "With fixed values\n");
212         printf("  --vxlan-decap: add vxlan_decap action to flow actions\n");
213 }
214
215 static void
216 args_parse(int argc, char **argv)
217 {
218         uint64_t pm;
219         char **argvopt;
220         char *token;
221         char *end;
222         int n, opt;
223         int opt_idx;
224         size_t i;
225
226         static const struct option_dict {
227                 const char *str;
228                 const uint64_t mask;
229                 uint64_t *map;
230                 uint8_t *map_idx;
231
232         } flow_options[] = {
233                 {
234                         .str = "ether",
235                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_ETH),
236                         .map = &flow_items[0],
237                         .map_idx = &items_idx
238                 },
239                 {
240                         .str = "ipv4",
241                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_IPV4),
242                         .map = &flow_items[0],
243                         .map_idx = &items_idx
244                 },
245                 {
246                         .str = "ipv6",
247                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_IPV6),
248                         .map = &flow_items[0],
249                         .map_idx = &items_idx
250                 },
251                 {
252                         .str = "vlan",
253                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_VLAN),
254                         .map = &flow_items[0],
255                         .map_idx = &items_idx
256                 },
257                 {
258                         .str = "tcp",
259                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_TCP),
260                         .map = &flow_items[0],
261                         .map_idx = &items_idx
262                 },
263                 {
264                         .str = "udp",
265                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_UDP),
266                         .map = &flow_items[0],
267                         .map_idx = &items_idx
268                 },
269                 {
270                         .str = "vxlan",
271                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_VXLAN),
272                         .map = &flow_items[0],
273                         .map_idx = &items_idx
274                 },
275                 {
276                         .str = "vxlan-gpe",
277                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_VXLAN_GPE),
278                         .map = &flow_items[0],
279                         .map_idx = &items_idx
280                 },
281                 {
282                         .str = "gre",
283                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_GRE),
284                         .map = &flow_items[0],
285                         .map_idx = &items_idx
286                 },
287                 {
288                         .str = "geneve",
289                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_GENEVE),
290                         .map = &flow_items[0],
291                         .map_idx = &items_idx
292                 },
293                 {
294                         .str = "gtp",
295                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_GTP),
296                         .map = &flow_items[0],
297                         .map_idx = &items_idx
298                 },
299                 {
300                         .str = "meta",
301                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_META),
302                         .map = &flow_items[0],
303                         .map_idx = &items_idx
304                 },
305                 {
306                         .str = "tag",
307                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_TAG),
308                         .map = &flow_items[0],
309                         .map_idx = &items_idx
310                 },
311                 {
312                         .str = "icmpv4",
313                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_ICMP),
314                         .map = &flow_items[0],
315                         .map_idx = &items_idx
316                 },
317                 {
318                         .str = "icmpv6",
319                         .mask = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_ICMP6),
320                         .map = &flow_items[0],
321                         .map_idx = &items_idx
322                 },
323                 {
324                         .str = "ingress",
325                         .mask = INGRESS,
326                         .map = &flow_attrs[0],
327                         .map_idx = &attrs_idx
328                 },
329                 {
330                         .str = "egress",
331                         .mask = EGRESS,
332                         .map = &flow_attrs[0],
333                         .map_idx = &attrs_idx
334                 },
335                 {
336                         .str = "transfer",
337                         .mask = TRANSFER,
338                         .map = &flow_attrs[0],
339                         .map_idx = &attrs_idx
340                 },
341                 {
342                         .str = "port-id",
343                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_PORT_ID),
344                         .map = &flow_actions[0],
345                         .map_idx = &actions_idx
346                 },
347                 {
348                         .str = "rss",
349                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_RSS),
350                         .map = &flow_actions[0],
351                         .map_idx = &actions_idx
352                 },
353                 {
354                         .str = "queue",
355                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_QUEUE),
356                         .map = &flow_actions[0],
357                         .map_idx = &actions_idx
358                 },
359                 {
360                         .str = "jump",
361                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_JUMP),
362                         .map = &flow_actions[0],
363                         .map_idx = &actions_idx
364                 },
365                 {
366                         .str = "mark",
367                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_MARK),
368                         .map = &flow_actions[0],
369                         .map_idx = &actions_idx
370                 },
371                 {
372                         .str = "count",
373                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_COUNT),
374                         .map = &flow_actions[0],
375                         .map_idx = &actions_idx
376                 },
377                 {
378                         .str = "set-meta",
379                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_SET_META),
380                         .map = &flow_actions[0],
381                         .map_idx = &actions_idx
382                 },
383                 {
384                         .str = "set-tag",
385                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_SET_TAG),
386                         .map = &flow_actions[0],
387                         .map_idx = &actions_idx
388                 },
389                 {
390                         .str = "drop",
391                         .mask = FLOW_ACTION_MASK(RTE_FLOW_ACTION_TYPE_DROP),
392                         .map = &flow_actions[0],
393                         .map_idx = &actions_idx
394                 },
395                 {
396                         .str = "set-src-mac",
397                         .mask = FLOW_ACTION_MASK(
398                                 RTE_FLOW_ACTION_TYPE_SET_MAC_SRC
399                         ),
400                         .map = &flow_actions[0],
401                         .map_idx = &actions_idx
402                 },
403                 {
404                         .str = "set-dst-mac",
405                         .mask = FLOW_ACTION_MASK(
406                                 RTE_FLOW_ACTION_TYPE_SET_MAC_DST
407                         ),
408                         .map = &flow_actions[0],
409                         .map_idx = &actions_idx
410                 },
411                 {
412                         .str = "set-src-ipv4",
413                         .mask = FLOW_ACTION_MASK(
414                                 RTE_FLOW_ACTION_TYPE_SET_IPV4_SRC
415                         ),
416                         .map = &flow_actions[0],
417                         .map_idx = &actions_idx
418                 },
419                 {
420                         .str = "set-dst-ipv4",
421                         .mask = FLOW_ACTION_MASK(
422                                 RTE_FLOW_ACTION_TYPE_SET_IPV4_DST
423                         ),
424                         .map = &flow_actions[0],
425                         .map_idx = &actions_idx
426                 },
427                 {
428                         .str = "set-src-ipv6",
429                         .mask = FLOW_ACTION_MASK(
430                                 RTE_FLOW_ACTION_TYPE_SET_IPV6_SRC
431                         ),
432                         .map = &flow_actions[0],
433                         .map_idx = &actions_idx
434                 },
435                 {
436                         .str = "set-dst-ipv6",
437                         .mask = FLOW_ACTION_MASK(
438                                 RTE_FLOW_ACTION_TYPE_SET_IPV6_DST
439                         ),
440                         .map = &flow_actions[0],
441                         .map_idx = &actions_idx
442                 },
443                 {
444                         .str = "set-src-tp",
445                         .mask = FLOW_ACTION_MASK(
446                                 RTE_FLOW_ACTION_TYPE_SET_TP_SRC
447                         ),
448                         .map = &flow_actions[0],
449                         .map_idx = &actions_idx
450                 },
451                 {
452                         .str = "set-dst-tp",
453                         .mask = FLOW_ACTION_MASK(
454                                 RTE_FLOW_ACTION_TYPE_SET_TP_DST
455                         ),
456                         .map = &flow_actions[0],
457                         .map_idx = &actions_idx
458                 },
459                 {
460                         .str = "inc-tcp-ack",
461                         .mask = FLOW_ACTION_MASK(
462                                 RTE_FLOW_ACTION_TYPE_INC_TCP_ACK
463                         ),
464                         .map = &flow_actions[0],
465                         .map_idx = &actions_idx
466                 },
467                 {
468                         .str = "dec-tcp-ack",
469                         .mask = FLOW_ACTION_MASK(
470                                 RTE_FLOW_ACTION_TYPE_DEC_TCP_ACK
471                         ),
472                         .map = &flow_actions[0],
473                         .map_idx = &actions_idx
474                 },
475                 {
476                         .str = "inc-tcp-seq",
477                         .mask = FLOW_ACTION_MASK(
478                                 RTE_FLOW_ACTION_TYPE_INC_TCP_SEQ
479                         ),
480                         .map = &flow_actions[0],
481                         .map_idx = &actions_idx
482                 },
483                 {
484                         .str = "dec-tcp-seq",
485                         .mask = FLOW_ACTION_MASK(
486                                 RTE_FLOW_ACTION_TYPE_DEC_TCP_SEQ
487                         ),
488                         .map = &flow_actions[0],
489                         .map_idx = &actions_idx
490                 },
491                 {
492                         .str = "set-ttl",
493                         .mask = FLOW_ACTION_MASK(
494                                 RTE_FLOW_ACTION_TYPE_SET_TTL
495                         ),
496                         .map = &flow_actions[0],
497                         .map_idx = &actions_idx
498                 },
499                 {
500                         .str = "dec-ttl",
501                         .mask = FLOW_ACTION_MASK(
502                                 RTE_FLOW_ACTION_TYPE_DEC_TTL
503                         ),
504                         .map = &flow_actions[0],
505                         .map_idx = &actions_idx
506                 },
507                 {
508                         .str = "set-ipv4-dscp",
509                         .mask = FLOW_ACTION_MASK(
510                                 RTE_FLOW_ACTION_TYPE_SET_IPV4_DSCP
511                         ),
512                         .map = &flow_actions[0],
513                         .map_idx = &actions_idx
514                 },
515                 {
516                         .str = "set-ipv6-dscp",
517                         .mask = FLOW_ACTION_MASK(
518                                 RTE_FLOW_ACTION_TYPE_SET_IPV6_DSCP
519                         ),
520                         .map = &flow_actions[0],
521                         .map_idx = &actions_idx
522                 },
523                 {
524                         .str = "flag",
525                         .mask = FLOW_ACTION_MASK(
526                                 RTE_FLOW_ACTION_TYPE_FLAG
527                         ),
528                         .map = &flow_actions[0],
529                         .map_idx = &actions_idx
530                 },
531                 {
532                         .str = "vxlan-encap",
533                         .mask = FLOW_ACTION_MASK(
534                                 RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP
535                         ),
536                         .map = &flow_actions[0],
537                         .map_idx = &actions_idx
538                 },
539                 {
540                         .str = "vxlan-decap",
541                         .mask = FLOW_ACTION_MASK(
542                                 RTE_FLOW_ACTION_TYPE_VXLAN_DECAP
543                         ),
544                         .map = &flow_actions[0],
545                         .map_idx = &actions_idx
546                 },
547         };
548
549         static const struct option lgopts[] = {
550                 /* Control */
551                 { "help",                       0, 0, 0 },
552                 { "rules-count",                1, 0, 0 },
553                 { "rules-batch",                1, 0, 0 },
554                 { "dump-iterations",            0, 0, 0 },
555                 { "deletion-rate",              0, 0, 0 },
556                 { "dump-socket-mem",            0, 0, 0 },
557                 { "enable-fwd",                 0, 0, 0 },
558                 { "portmask",                   1, 0, 0 },
559                 { "cores",                      1, 0, 0 },
560                 /* Attributes */
561                 { "ingress",                    0, 0, 0 },
562                 { "egress",                     0, 0, 0 },
563                 { "transfer",                   0, 0, 0 },
564                 { "group",                      1, 0, 0 },
565                 /* Items */
566                 { "ether",                      0, 0, 0 },
567                 { "vlan",                       0, 0, 0 },
568                 { "ipv4",                       0, 0, 0 },
569                 { "ipv6",                       0, 0, 0 },
570                 { "tcp",                        0, 0, 0 },
571                 { "udp",                        0, 0, 0 },
572                 { "vxlan",                      0, 0, 0 },
573                 { "vxlan-gpe",                  0, 0, 0 },
574                 { "gre",                        0, 0, 0 },
575                 { "geneve",                     0, 0, 0 },
576                 { "gtp",                        0, 0, 0 },
577                 { "meta",                       0, 0, 0 },
578                 { "tag",                        0, 0, 0 },
579                 { "icmpv4",                     0, 0, 0 },
580                 { "icmpv6",                     0, 0, 0 },
581                 /* Actions */
582                 { "port-id",                    0, 0, 0 },
583                 { "rss",                        0, 0, 0 },
584                 { "queue",                      0, 0, 0 },
585                 { "jump",                       0, 0, 0 },
586                 { "mark",                       0, 0, 0 },
587                 { "count",                      0, 0, 0 },
588                 { "set-meta",                   0, 0, 0 },
589                 { "set-tag",                    0, 0, 0 },
590                 { "drop",                       0, 0, 0 },
591                 { "hairpin-queue",              1, 0, 0 },
592                 { "hairpin-rss",                1, 0, 0 },
593                 { "set-src-mac",                0, 0, 0 },
594                 { "set-dst-mac",                0, 0, 0 },
595                 { "set-src-ipv4",               0, 0, 0 },
596                 { "set-dst-ipv4",               0, 0, 0 },
597                 { "set-src-ipv6",               0, 0, 0 },
598                 { "set-dst-ipv6",               0, 0, 0 },
599                 { "set-src-tp",                 0, 0, 0 },
600                 { "set-dst-tp",                 0, 0, 0 },
601                 { "inc-tcp-ack",                0, 0, 0 },
602                 { "dec-tcp-ack",                0, 0, 0 },
603                 { "inc-tcp-seq",                0, 0, 0 },
604                 { "dec-tcp-seq",                0, 0, 0 },
605                 { "set-ttl",                    0, 0, 0 },
606                 { "dec-ttl",                    0, 0, 0 },
607                 { "set-ipv4-dscp",              0, 0, 0 },
608                 { "set-ipv6-dscp",              0, 0, 0 },
609                 { "flag",                       0, 0, 0 },
610                 { "raw-encap",                  1, 0, 0 },
611                 { "raw-decap",                  1, 0, 0 },
612                 { "vxlan-encap",                0, 0, 0 },
613                 { "vxlan-decap",                0, 0, 0 },
614         };
615
616         RTE_ETH_FOREACH_DEV(i)
617                 ports_mask |= 1 << i;
618
619         hairpin_queues_num = 0;
620         argvopt = argv;
621
622         printf(":: Flow -> ");
623         while ((opt = getopt_long(argc, argvopt, "",
624                                 lgopts, &opt_idx)) != EOF) {
625                 switch (opt) {
626                 case 0:
627                         if (strcmp(lgopts[opt_idx].name, "help") == 0) {
628                                 usage(argv[0]);
629                                 rte_exit(EXIT_SUCCESS, "Displayed help\n");
630                         }
631
632                         if (strcmp(lgopts[opt_idx].name, "group") == 0) {
633                                 n = atoi(optarg);
634                                 if (n >= 0)
635                                         flow_group = n;
636                                 else
637                                         rte_exit(EXIT_SUCCESS,
638                                                 "flow group should be >= 0\n");
639                                 printf("group %d / ", flow_group);
640                         }
641
642                         for (i = 0; i < RTE_DIM(flow_options); i++)
643                                 if (strcmp(lgopts[opt_idx].name,
644                                                 flow_options[i].str) == 0) {
645                                         flow_options[i].map[
646                                         (*flow_options[i].map_idx)++] =
647                                                 flow_options[i].mask;
648                                         printf("%s / ", flow_options[i].str);
649                                 }
650
651                         if (strcmp(lgopts[opt_idx].name,
652                                         "hairpin-rss") == 0) {
653                                 n = atoi(optarg);
654                                 if (n > 0)
655                                         hairpin_queues_num = n;
656                                 else
657                                         rte_exit(EXIT_SUCCESS,
658                                                 "Hairpin queues should be > 0\n");
659
660                                 flow_actions[actions_idx++] =
661                                         HAIRPIN_RSS_ACTION;
662                                 printf("hairpin-rss / ");
663                         }
664                         if (strcmp(lgopts[opt_idx].name,
665                                         "hairpin-queue") == 0) {
666                                 n = atoi(optarg);
667                                 if (n > 0)
668                                         hairpin_queues_num = n;
669                                 else
670                                         rte_exit(EXIT_SUCCESS,
671                                                 "Hairpin queues should be > 0\n");
672
673                                 flow_actions[actions_idx++] =
674                                         HAIRPIN_QUEUE_ACTION;
675                                 printf("hairpin-queue / ");
676                         }
677
678                         if (strcmp(lgopts[opt_idx].name, "raw-encap") == 0) {
679                                 printf("raw-encap ");
680                                 flow_actions[actions_idx++] =
681                                         FLOW_ITEM_MASK(
682                                                 RTE_FLOW_ACTION_TYPE_RAW_ENCAP
683                                         );
684
685                                 token = strtok(optarg, ",");
686                                 while (token != NULL) {
687                                         for (i = 0; i < RTE_DIM(flow_options); i++) {
688                                                 if (strcmp(flow_options[i].str, token) == 0) {
689                                                         printf("%s,", token);
690                                                         encap_data |= flow_options[i].mask;
691                                                         break;
692                                                 }
693                                                 /* Reached last item with no match */
694                                                 if (i == (RTE_DIM(flow_options) - 1)) {
695                                                         fprintf(stderr, "Invalid encap item: %s\n", token);
696                                                         usage(argv[0]);
697                                                         rte_exit(EXIT_SUCCESS, "Invalid encap item\n");
698                                                 }
699                                         }
700                                         token = strtok(NULL, ",");
701                                 }
702                                 printf(" / ");
703                         }
704                         if (strcmp(lgopts[opt_idx].name, "raw-decap") == 0) {
705                                 printf("raw-decap ");
706                                 flow_actions[actions_idx++] =
707                                         FLOW_ITEM_MASK(
708                                                 RTE_FLOW_ACTION_TYPE_RAW_DECAP
709                                         );
710
711                                 token = strtok(optarg, ",");
712                                 while (token != NULL) {
713                                         for (i = 0; i < RTE_DIM(flow_options); i++) {
714                                                 if (strcmp(flow_options[i].str, token) == 0) {
715                                                         printf("%s,", token);
716                                                         encap_data |= flow_options[i].mask;
717                                                         break;
718                                                 }
719                                                 /* Reached last item with no match */
720                                                 if (i == (RTE_DIM(flow_options) - 1)) {
721                                                         fprintf(stderr, "Invalid decap item: %s\n", token);
722                                                         usage(argv[0]);
723                                                         rte_exit(EXIT_SUCCESS, "Invalid decap item\n");
724                                                 }
725                                         }
726                                         token = strtok(NULL, ",");
727                                 }
728                                 printf(" / ");
729                         }
730                         /* Control */
731                         if (strcmp(lgopts[opt_idx].name,
732                                         "rules-batch") == 0) {
733                                 n = atoi(optarg);
734                                 if (n >= DEFAULT_RULES_BATCH)
735                                         rules_batch = n;
736                                 else {
737                                         printf("\n\nrules_batch should be >= %d\n",
738                                                 DEFAULT_RULES_BATCH);
739                                         rte_exit(EXIT_SUCCESS, " ");
740                                 }
741                         }
742                         if (strcmp(lgopts[opt_idx].name,
743                                         "rules-count") == 0) {
744                                 n = atoi(optarg);
745                                 if (n >= (int) rules_batch)
746                                         rules_count = n;
747                                 else {
748                                         printf("\n\nrules_count should be >= %d\n",
749                                                 rules_batch);
750                                 }
751                         }
752                         if (strcmp(lgopts[opt_idx].name,
753                                         "dump-iterations") == 0)
754                                 dump_iterations = true;
755                         if (strcmp(lgopts[opt_idx].name,
756                                         "deletion-rate") == 0)
757                                 delete_flag = true;
758                         if (strcmp(lgopts[opt_idx].name,
759                                         "dump-socket-mem") == 0)
760                                 dump_socket_mem_flag = true;
761                         if (strcmp(lgopts[opt_idx].name,
762                                         "enable-fwd") == 0)
763                                 enable_fwd = true;
764                         if (strcmp(lgopts[opt_idx].name,
765                                         "portmask") == 0) {
766                                 /* parse hexadecimal string */
767                                 end = NULL;
768                                 pm = strtoull(optarg, &end, 16);
769                                 if ((optarg[0] == '\0') || (end == NULL) || (*end != '\0'))
770                                         rte_exit(EXIT_FAILURE, "Invalid fwd port mask\n");
771                                 ports_mask = pm;
772                         }
773                         if (strcmp(lgopts[opt_idx].name, "cores") == 0) {
774                                 n = atoi(optarg);
775                                 if ((int) rte_lcore_count() <= n) {
776                                         printf("\nError: you need %d cores to run on multi-cores\n"
777                                                 "Existing cores are: %d\n", n, rte_lcore_count());
778                                         rte_exit(EXIT_FAILURE, " ");
779                                 }
780                                 if (n <= RTE_MAX_LCORE && n > 0)
781                                         mc_pool.cores_count = n;
782                                 else {
783                                         printf("Error: cores count must be > 0 "
784                                                 " and < %d\n", RTE_MAX_LCORE);
785                                         rte_exit(EXIT_FAILURE, " ");
786                                 }
787                         }
788                         break;
789                 default:
790                         fprintf(stderr, "Invalid option: %s\n", argv[optind]);
791                         usage(argv[0]);
792                         rte_exit(EXIT_SUCCESS, "Invalid option\n");
793                         break;
794                 }
795         }
796         printf("end_flow\n");
797 }
798
799 /* Dump the socket memory statistics on console */
800 static size_t
801 dump_socket_mem(FILE *f)
802 {
803         struct rte_malloc_socket_stats socket_stats;
804         unsigned int i = 0;
805         size_t total = 0;
806         size_t alloc = 0;
807         size_t free = 0;
808         unsigned int n_alloc = 0;
809         unsigned int n_free = 0;
810         bool active_nodes = false;
811
812
813         for (i = 0; i < RTE_MAX_NUMA_NODES; i++) {
814                 if (rte_malloc_get_socket_stats(i, &socket_stats) ||
815                     !socket_stats.heap_totalsz_bytes)
816                         continue;
817                 active_nodes = true;
818                 total += socket_stats.heap_totalsz_bytes;
819                 alloc += socket_stats.heap_allocsz_bytes;
820                 free += socket_stats.heap_freesz_bytes;
821                 n_alloc += socket_stats.alloc_count;
822                 n_free += socket_stats.free_count;
823                 if (dump_socket_mem_flag) {
824                         fprintf(f, "::::::::::::::::::::::::::::::::::::::::");
825                         fprintf(f,
826                                 "\nSocket %u:\nsize(M) total: %.6lf\nalloc:"
827                                 " %.6lf(%.3lf%%)\nfree: %.6lf"
828                                 "\nmax: %.6lf"
829                                 "\ncount alloc: %u\nfree: %u\n",
830                                 i,
831                                 socket_stats.heap_totalsz_bytes / 1.0e6,
832                                 socket_stats.heap_allocsz_bytes / 1.0e6,
833                                 (double)socket_stats.heap_allocsz_bytes * 100 /
834                                 (double)socket_stats.heap_totalsz_bytes,
835                                 socket_stats.heap_freesz_bytes / 1.0e6,
836                                 socket_stats.greatest_free_size / 1.0e6,
837                                 socket_stats.alloc_count,
838                                 socket_stats.free_count);
839                                 fprintf(f, "::::::::::::::::::::::::::::::::::::::::");
840                 }
841         }
842         if (dump_socket_mem_flag && active_nodes) {
843                 fprintf(f,
844                         "\nTotal: size(M)\ntotal: %.6lf"
845                         "\nalloc: %.6lf(%.3lf%%)\nfree: %.6lf"
846                         "\ncount alloc: %u\nfree: %u\n",
847                         total / 1.0e6, alloc / 1.0e6,
848                         (double)alloc * 100 / (double)total, free / 1.0e6,
849                         n_alloc, n_free);
850                 fprintf(f, "::::::::::::::::::::::::::::::::::::::::\n");
851         }
852         return alloc;
853 }
854
855 static void
856 print_flow_error(struct rte_flow_error error)
857 {
858         printf("Flow can't be created %d message: %s\n",
859                 error.type,
860                 error.message ? error.message : "(no stated reason)");
861 }
862
863 static inline void
864 print_rules_batches(double *cpu_time_per_batch)
865 {
866         uint8_t idx;
867         double delta;
868         double rate;
869
870         for (idx = 0; idx < MAX_BATCHES_COUNT; idx++) {
871                 if (!cpu_time_per_batch[idx])
872                         break;
873                 delta = (double)(rules_batch / cpu_time_per_batch[idx]);
874                 rate = delta / 1000; /* Save rate in K unit. */
875                 printf(":: Rules batch #%d: %d rules "
876                         "in %f sec[ Rate = %f K Rule/Sec ]\n",
877                         idx, rules_batch,
878                         cpu_time_per_batch[idx], rate);
879         }
880 }
881
882 static inline void
883 destroy_flows(int port_id, uint8_t core_id, struct rte_flow **flows_list)
884 {
885         struct rte_flow_error error;
886         clock_t start_batch, end_batch;
887         double cpu_time_used = 0;
888         double deletion_rate;
889         double cpu_time_per_batch[MAX_BATCHES_COUNT] = { 0 };
890         double delta;
891         uint32_t i;
892         int rules_batch_idx;
893         int rules_count_per_core;
894
895         rules_count_per_core = rules_count / mc_pool.cores_count;
896
897         start_batch = rte_rdtsc();
898         for (i = 0; i < (uint32_t) rules_count_per_core; i++) {
899                 if (flows_list[i] == 0)
900                         break;
901
902                 memset(&error, 0x33, sizeof(error));
903                 if (rte_flow_destroy(port_id, flows_list[i], &error)) {
904                         print_flow_error(error);
905                         rte_exit(EXIT_FAILURE, "Error in deleting flow");
906                 }
907
908                 /*
909                  * Save the deletion rate for rules batch.
910                  * Check if the deletion reached the rules
911                  * patch counter, then save the deletion rate
912                  * for this batch.
913                  */
914                 if (!((i + 1) % rules_batch)) {
915                         end_batch = rte_rdtsc();
916                         delta = (double) (end_batch - start_batch);
917                         rules_batch_idx = ((i + 1) / rules_batch) - 1;
918                         cpu_time_per_batch[rules_batch_idx] = delta / rte_get_tsc_hz();
919                         cpu_time_used += cpu_time_per_batch[rules_batch_idx];
920                         start_batch = rte_rdtsc();
921                 }
922         }
923
924         /* Print deletion rates for all batches */
925         if (dump_iterations)
926                 print_rules_batches(cpu_time_per_batch);
927
928         /* Deletion rate for all rules */
929         deletion_rate = ((double) (rules_count_per_core / cpu_time_used) / 1000);
930         printf(":: Port %d :: Core %d :: Rules deletion rate -> %f K Rule/Sec\n",
931                 port_id, core_id, deletion_rate);
932         printf(":: Port %d :: Core %d :: The time for deleting %d rules is %f seconds\n",
933                 port_id, core_id, rules_count_per_core, cpu_time_used);
934
935         mc_pool.create_flow.deletion[port_id][core_id] = cpu_time_used;
936 }
937
938 static struct rte_flow **
939 insert_flows(int port_id, uint8_t core_id)
940 {
941         struct rte_flow **flows_list;
942         struct rte_flow_error error;
943         clock_t start_batch, end_batch;
944         double cpu_time_used;
945         double insertion_rate;
946         double cpu_time_per_batch[MAX_BATCHES_COUNT] = { 0 };
947         double delta;
948         uint32_t flow_index;
949         uint32_t counter, start_counter = 0, end_counter;
950         uint64_t global_items[MAX_ITEMS_NUM] = { 0 };
951         uint64_t global_actions[MAX_ACTIONS_NUM] = { 0 };
952         int rules_batch_idx;
953         int rules_count_per_core;
954
955         rules_count_per_core = rules_count / mc_pool.cores_count;
956
957         /* Set boundaries of rules for each core. */
958         if (core_id)
959                 start_counter = core_id * rules_count_per_core;
960         end_counter = (core_id + 1) * rules_count_per_core;
961
962         global_items[0] = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_ETH);
963         global_actions[0] = FLOW_ITEM_MASK(RTE_FLOW_ACTION_TYPE_JUMP);
964
965         flows_list = rte_zmalloc("flows_list",
966                 (sizeof(struct rte_flow *) * rules_count_per_core) + 1, 0);
967         if (flows_list == NULL)
968                 rte_exit(EXIT_FAILURE, "No Memory available!");
969
970         cpu_time_used = 0;
971         flow_index = 0;
972         if (flow_group > 0 && core_id == 0) {
973                 /*
974                  * Create global rule to jump into flow_group,
975                  * this way the app will avoid the default rules.
976                  *
977                  * This rule will be created only once.
978                  *
979                  * Global rule:
980                  * group 0 eth / end actions jump group <flow_group>
981                  */
982                 flow = generate_flow(port_id, 0, flow_attrs,
983                         global_items, global_actions,
984                         flow_group, 0, 0, 0, 0, core_id, &error);
985
986                 if (flow == NULL) {
987                         print_flow_error(error);
988                         rte_exit(EXIT_FAILURE, "error in creating flow");
989                 }
990                 flows_list[flow_index++] = flow;
991         }
992
993         start_batch = rte_rdtsc();
994         for (counter = start_counter; counter < end_counter; counter++) {
995                 flow = generate_flow(port_id, flow_group,
996                         flow_attrs, flow_items, flow_actions,
997                         JUMP_ACTION_TABLE, counter,
998                         hairpin_queues_num,
999                         encap_data, decap_data,
1000                         core_id, &error);
1001
1002                 if (force_quit)
1003                         counter = end_counter;
1004
1005                 if (!flow) {
1006                         print_flow_error(error);
1007                         rte_exit(EXIT_FAILURE, "error in creating flow");
1008                 }
1009
1010                 flows_list[flow_index++] = flow;
1011
1012                 /*
1013                  * Save the insertion rate for rules batch.
1014                  * Check if the insertion reached the rules
1015                  * patch counter, then save the insertion rate
1016                  * for this batch.
1017                  */
1018                 if (!((counter + 1) % rules_batch)) {
1019                         end_batch = rte_rdtsc();
1020                         delta = (double) (end_batch - start_batch);
1021                         rules_batch_idx = ((counter + 1) / rules_batch) - 1;
1022                         cpu_time_per_batch[rules_batch_idx] = delta / rte_get_tsc_hz();
1023                         cpu_time_used += cpu_time_per_batch[rules_batch_idx];
1024                         start_batch = rte_rdtsc();
1025                 }
1026         }
1027
1028         /* Print insertion rates for all batches */
1029         if (dump_iterations)
1030                 print_rules_batches(cpu_time_per_batch);
1031
1032         printf(":: Port %d :: Core %d boundaries :: start @[%d] - end @[%d]\n",
1033                 port_id, core_id, start_counter, end_counter - 1);
1034
1035         /* Insertion rate for all rules in one core */
1036         insertion_rate = ((double) (rules_count_per_core / cpu_time_used) / 1000);
1037         printf(":: Port %d :: Core %d :: Rules insertion rate -> %f K Rule/Sec\n",
1038                 port_id, core_id, insertion_rate);
1039         printf(":: Port %d :: Core %d :: The time for creating %d in rules %f seconds\n",
1040                 port_id, core_id, rules_count_per_core, cpu_time_used);
1041
1042         mc_pool.create_flow.insertion[port_id][core_id] = cpu_time_used;
1043         return flows_list;
1044 }
1045
1046 static void
1047 flows_handler(uint8_t core_id)
1048 {
1049         struct rte_flow **flows_list;
1050         uint16_t nr_ports;
1051         int port_id;
1052
1053         nr_ports = rte_eth_dev_count_avail();
1054
1055         if (rules_batch > rules_count)
1056                 rules_batch = rules_count;
1057
1058         printf(":: Rules Count per port: %d\n\n", rules_count);
1059
1060         for (port_id = 0; port_id < nr_ports; port_id++) {
1061                 /* If port outside portmask */
1062                 if (!((ports_mask >> port_id) & 0x1))
1063                         continue;
1064
1065                 /* Insertion part. */
1066                 mc_pool.last_alloc[core_id] = (int64_t)dump_socket_mem(stdout);
1067                 flows_list = insert_flows(port_id, core_id);
1068                 if (flows_list == NULL)
1069                         rte_exit(EXIT_FAILURE, "Error: Insertion Failed!\n");
1070                 mc_pool.current_alloc[core_id] = (int64_t)dump_socket_mem(stdout);
1071
1072                 /* Deletion part. */
1073                 if (delete_flag)
1074                         destroy_flows(port_id, core_id, flows_list);
1075         }
1076 }
1077
1078 static void
1079 dump_used_cpu_time(const char *item,
1080                 uint16_t port, struct used_cpu_time *used_time)
1081 {
1082         uint32_t i;
1083         /* Latency: total count of rte rules divided
1084          * over max time used by thread between all
1085          * threads time.
1086          *
1087          * Throughput: total count of rte rules divided
1088          * over the average of the time cosumed by all
1089          * threads time.
1090          */
1091         double insertion_latency_time;
1092         double insertion_throughput_time;
1093         double deletion_latency_time;
1094         double deletion_throughput_time;
1095         double insertion_latency, insertion_throughput;
1096         double deletion_latency, deletion_throughput;
1097
1098         /* Save first insertion/deletion rates from first thread.
1099          * Start comparing with all threads, if any thread used
1100          * time more than current saved, replace it.
1101          *
1102          * Thus in the end we will have the max time used for
1103          * insertion/deletion by one thread.
1104          *
1105          * As for memory consumption, save the min of all threads
1106          * of last alloc, and save the max for all threads for
1107          * current alloc.
1108          */
1109
1110         insertion_latency_time = used_time->insertion[port][0];
1111         deletion_latency_time = used_time->deletion[port][0];
1112         insertion_throughput_time = used_time->insertion[port][0];
1113         deletion_throughput_time = used_time->deletion[port][0];
1114
1115         i = mc_pool.cores_count;
1116         while (i-- > 1) {
1117                 insertion_throughput_time += used_time->insertion[port][i];
1118                 deletion_throughput_time += used_time->deletion[port][i];
1119                 if (insertion_latency_time < used_time->insertion[port][i])
1120                         insertion_latency_time = used_time->insertion[port][i];
1121                 if (deletion_latency_time < used_time->deletion[port][i])
1122                         deletion_latency_time = used_time->deletion[port][i];
1123         }
1124
1125         insertion_latency = ((double) (mc_pool.rules_count
1126                                 / insertion_latency_time) / 1000);
1127         deletion_latency = ((double) (mc_pool.rules_count
1128                                 / deletion_latency_time) / 1000);
1129
1130         insertion_throughput_time /= mc_pool.cores_count;
1131         deletion_throughput_time /= mc_pool.cores_count;
1132         insertion_throughput = ((double) (mc_pool.rules_count
1133                                 / insertion_throughput_time) / 1000);
1134         deletion_throughput = ((double) (mc_pool.rules_count
1135                                 / deletion_throughput_time) / 1000);
1136
1137         /* Latency stats */
1138         printf("\n%s\n:: [Latency | Insertion] All Cores :: Port %d :: ",
1139                 item, port);
1140         printf("Total flows insertion rate -> %f K Rules/Sec\n",
1141                 insertion_latency);
1142         printf(":: [Latency | Insertion] All Cores :: Port %d :: ", port);
1143         printf("The time for creating %d rules is %f seconds\n",
1144                 mc_pool.rules_count, insertion_latency_time);
1145
1146         /* Throughput stats */
1147         printf(":: [Throughput | Insertion] All Cores :: Port %d :: ", port);
1148         printf("Total flows insertion rate -> %f K Rules/Sec\n",
1149                 insertion_throughput);
1150         printf(":: [Throughput | Insertion] All Cores :: Port %d :: ", port);
1151         printf("The average time for creating %d rules is %f seconds\n",
1152                 mc_pool.rules_count, insertion_throughput_time);
1153
1154         if (delete_flag) {
1155         /* Latency stats */
1156                 printf(":: [Latency | Deletion] All Cores :: Port %d :: Total "
1157                         "deletion rate -> %f K Rules/Sec\n",
1158                         port, deletion_latency);
1159                 printf(":: [Latency | Deletion] All Cores :: Port %d :: ",
1160                         port);
1161                 printf("The time for deleting %d rules is %f seconds\n",
1162                         mc_pool.rules_count, deletion_latency_time);
1163
1164                 /* Throughput stats */
1165                 printf(":: [Throughput | Deletion] All Cores :: Port %d :: Total "
1166                         "deletion rate -> %f K Rules/Sec\n",
1167                         port, deletion_throughput);
1168                 printf(":: [Throughput | Deletion] All Cores :: Port %d :: ",
1169                         port);
1170                 printf("The average time for deleting %d rules is %f seconds\n",
1171                         mc_pool.rules_count, deletion_throughput_time);
1172         }
1173 }
1174
1175 static void
1176 dump_used_mem(uint16_t port)
1177 {
1178         uint32_t i;
1179         int64_t last_alloc, current_alloc;
1180         int flow_size_in_bytes;
1181
1182         last_alloc = mc_pool.last_alloc[0];
1183         current_alloc = mc_pool.current_alloc[0];
1184
1185         i = mc_pool.cores_count;
1186         while (i-- > 1) {
1187                 if (last_alloc > mc_pool.last_alloc[i])
1188                         last_alloc = mc_pool.last_alloc[i];
1189                 if (current_alloc < mc_pool.current_alloc[i])
1190                         current_alloc = mc_pool.current_alloc[i];
1191         }
1192
1193         flow_size_in_bytes = (current_alloc - last_alloc) / mc_pool.rules_count;
1194         printf("\n:: Port %d :: rte_flow size in DPDK layer: %d Bytes\n",
1195                 port, flow_size_in_bytes);
1196 }
1197
1198 static int
1199 run_rte_flow_handler_cores(void *data __rte_unused)
1200 {
1201         uint16_t port;
1202         int lcore_counter = 0;
1203         int lcore_id = rte_lcore_id();
1204         int i;
1205
1206         RTE_LCORE_FOREACH(i) {
1207                 /*  If core not needed return. */
1208                 if (lcore_id == i) {
1209                         printf(":: lcore %d mapped with index %d\n", lcore_id, lcore_counter);
1210                         if (lcore_counter >= (int) mc_pool.cores_count)
1211                                 return 0;
1212                         break;
1213                 }
1214                 lcore_counter++;
1215         }
1216         lcore_id = lcore_counter;
1217
1218         if (lcore_id >= (int) mc_pool.cores_count)
1219                 return 0;
1220
1221         mc_pool.rules_count = rules_count;
1222
1223         flows_handler(lcore_id);
1224
1225         /* Only main core to print total results. */
1226         if (lcore_id != 0)
1227                 return 0;
1228
1229         /* Make sure all cores finished insertion/deletion process. */
1230         rte_eal_mp_wait_lcore();
1231
1232         RTE_ETH_FOREACH_DEV(port) {
1233
1234                 dump_used_cpu_time("Flows:",
1235                         port, &mc_pool.create_flow);
1236                 dump_used_mem(port);
1237         }
1238
1239         return 0;
1240 }
1241
1242 static void
1243 signal_handler(int signum)
1244 {
1245         if (signum == SIGINT || signum == SIGTERM) {
1246                 printf("\n\nSignal %d received, preparing to exit...\n",
1247                                         signum);
1248                 printf("Error: Stats are wrong due to sudden signal!\n\n");
1249                 force_quit = true;
1250         }
1251 }
1252
1253 static inline uint16_t
1254 do_rx(struct lcore_info *li, uint16_t rx_port, uint16_t rx_queue)
1255 {
1256         uint16_t cnt = 0;
1257         cnt = rte_eth_rx_burst(rx_port, rx_queue, li->pkts, MAX_PKT_BURST);
1258         li->rx_pkts += cnt;
1259         return cnt;
1260 }
1261
1262 static inline void
1263 do_tx(struct lcore_info *li, uint16_t cnt, uint16_t tx_port,
1264                         uint16_t tx_queue)
1265 {
1266         uint16_t nr_tx = 0;
1267         uint16_t i;
1268
1269         nr_tx = rte_eth_tx_burst(tx_port, tx_queue, li->pkts, cnt);
1270         li->tx_pkts  += nr_tx;
1271         li->tx_drops += cnt - nr_tx;
1272
1273         for (i = nr_tx; i < cnt; i++)
1274                 rte_pktmbuf_free(li->pkts[i]);
1275 }
1276
1277 /*
1278  * Method to convert numbers into pretty numbers that easy
1279  * to read. The design here is to add comma after each three
1280  * digits and set all of this inside buffer.
1281  *
1282  * For example if n = 1799321, the output will be
1283  * 1,799,321 after this method which is easier to read.
1284  */
1285 static char *
1286 pretty_number(uint64_t n, char *buf)
1287 {
1288         char p[6][4];
1289         int i = 0;
1290         int off = 0;
1291
1292         while (n > 1000) {
1293                 sprintf(p[i], "%03d", (int)(n % 1000));
1294                 n /= 1000;
1295                 i += 1;
1296         }
1297
1298         sprintf(p[i++], "%d", (int)n);
1299
1300         while (i--)
1301                 off += sprintf(buf + off, "%s,", p[i]);
1302         buf[strlen(buf) - 1] = '\0';
1303
1304         return buf;
1305 }
1306
1307 static void
1308 packet_per_second_stats(void)
1309 {
1310         struct lcore_info *old;
1311         struct lcore_info *li, *oli;
1312         int nr_lines = 0;
1313         int i;
1314
1315         old = rte_zmalloc("old",
1316                 sizeof(struct lcore_info) * RTE_MAX_LCORE, 0);
1317         if (old == NULL)
1318                 rte_exit(EXIT_FAILURE, "No Memory available!");
1319
1320         memcpy(old, lcore_infos,
1321                 sizeof(struct lcore_info) * RTE_MAX_LCORE);
1322
1323         while (!force_quit) {
1324                 uint64_t total_tx_pkts = 0;
1325                 uint64_t total_rx_pkts = 0;
1326                 uint64_t total_tx_drops = 0;
1327                 uint64_t tx_delta, rx_delta, drops_delta;
1328                 char buf[3][32];
1329                 int nr_valid_core = 0;
1330
1331                 sleep(1);
1332
1333                 if (nr_lines) {
1334                         char go_up_nr_lines[16];
1335
1336                         sprintf(go_up_nr_lines, "%c[%dA\r", 27, nr_lines);
1337                         printf("%s\r", go_up_nr_lines);
1338                 }
1339
1340                 printf("\n%6s %16s %16s %16s\n", "core", "tx", "tx drops", "rx");
1341                 printf("%6s %16s %16s %16s\n", "------", "----------------",
1342                         "----------------", "----------------");
1343                 nr_lines = 3;
1344                 for (i = 0; i < RTE_MAX_LCORE; i++) {
1345                         li  = &lcore_infos[i];
1346                         oli = &old[i];
1347                         if (li->mode != LCORE_MODE_PKT)
1348                                 continue;
1349
1350                         tx_delta    = li->tx_pkts  - oli->tx_pkts;
1351                         rx_delta    = li->rx_pkts  - oli->rx_pkts;
1352                         drops_delta = li->tx_drops - oli->tx_drops;
1353                         printf("%6d %16s %16s %16s\n", i,
1354                                 pretty_number(tx_delta,    buf[0]),
1355                                 pretty_number(drops_delta, buf[1]),
1356                                 pretty_number(rx_delta,    buf[2]));
1357
1358                         total_tx_pkts  += tx_delta;
1359                         total_rx_pkts  += rx_delta;
1360                         total_tx_drops += drops_delta;
1361
1362                         nr_valid_core++;
1363                         nr_lines += 1;
1364                 }
1365
1366                 if (nr_valid_core > 1) {
1367                         printf("%6s %16s %16s %16s\n", "total",
1368                                 pretty_number(total_tx_pkts,  buf[0]),
1369                                 pretty_number(total_tx_drops, buf[1]),
1370                                 pretty_number(total_rx_pkts,  buf[2]));
1371                         nr_lines += 1;
1372                 }
1373
1374                 memcpy(old, lcore_infos,
1375                         sizeof(struct lcore_info) * RTE_MAX_LCORE);
1376         }
1377 }
1378
1379 static int
1380 start_forwarding(void *data __rte_unused)
1381 {
1382         int lcore = rte_lcore_id();
1383         int stream_id;
1384         uint16_t cnt;
1385         struct lcore_info *li = &lcore_infos[lcore];
1386
1387         if (!li->mode)
1388                 return 0;
1389
1390         if (li->mode == LCORE_MODE_STATS) {
1391                 printf(":: started stats on lcore %u\n", lcore);
1392                 packet_per_second_stats();
1393                 return 0;
1394         }
1395
1396         while (!force_quit)
1397                 for (stream_id = 0; stream_id < MAX_STREAMS; stream_id++) {
1398                         if (li->streams[stream_id].rx_port == -1)
1399                                 continue;
1400
1401                         cnt = do_rx(li,
1402                                         li->streams[stream_id].rx_port,
1403                                         li->streams[stream_id].rx_queue);
1404                         if (cnt)
1405                                 do_tx(li, cnt,
1406                                         li->streams[stream_id].tx_port,
1407                                         li->streams[stream_id].tx_queue);
1408                 }
1409         return 0;
1410 }
1411
1412 static void
1413 init_lcore_info(void)
1414 {
1415         int i, j;
1416         unsigned int lcore;
1417         uint16_t nr_port;
1418         uint16_t queue;
1419         int port;
1420         int stream_id = 0;
1421         int streams_per_core;
1422         int unassigned_streams;
1423         int nb_fwd_streams;
1424         nr_port = rte_eth_dev_count_avail();
1425
1426         /* First logical core is reserved for stats printing */
1427         lcore = rte_get_next_lcore(-1, 0, 0);
1428         lcore_infos[lcore].mode = LCORE_MODE_STATS;
1429
1430         /*
1431          * Initialize all cores
1432          * All cores at first must have -1 value in all streams
1433          * This means that this stream is not used, or not set
1434          * yet.
1435          */
1436         for (i = 0; i < RTE_MAX_LCORE; i++)
1437                 for (j = 0; j < MAX_STREAMS; j++) {
1438                         lcore_infos[i].streams[j].tx_port = -1;
1439                         lcore_infos[i].streams[j].rx_port = -1;
1440                         lcore_infos[i].streams[j].tx_queue = -1;
1441                         lcore_infos[i].streams[j].rx_queue = -1;
1442                         lcore_infos[i].streams_nb = 0;
1443                 }
1444
1445         /*
1446          * Calculate the total streams count.
1447          * Also distribute those streams count between the available
1448          * logical cores except first core, since it's reserved for
1449          * stats prints.
1450          */
1451         nb_fwd_streams = nr_port * RXQ_NUM;
1452         if ((int)(nb_lcores - 1) >= nb_fwd_streams)
1453                 for (i = 0; i < (int)(nb_lcores - 1); i++) {
1454                         lcore = rte_get_next_lcore(lcore, 0, 0);
1455                         lcore_infos[lcore].streams_nb = 1;
1456                 }
1457         else {
1458                 streams_per_core = nb_fwd_streams / (nb_lcores - 1);
1459                 unassigned_streams = nb_fwd_streams % (nb_lcores - 1);
1460                 for (i = 0; i < (int)(nb_lcores - 1); i++) {
1461                         lcore = rte_get_next_lcore(lcore, 0, 0);
1462                         lcore_infos[lcore].streams_nb = streams_per_core;
1463                         if (unassigned_streams) {
1464                                 lcore_infos[lcore].streams_nb++;
1465                                 unassigned_streams--;
1466                         }
1467                 }
1468         }
1469
1470         /*
1471          * Set the streams for the cores according to each logical
1472          * core stream count.
1473          * The streams is built on the design of what received should
1474          * forward as well, this means that if you received packets on
1475          * port 0 queue 0 then the same queue should forward the
1476          * packets, using the same logical core.
1477          */
1478         lcore = rte_get_next_lcore(-1, 0, 0);
1479         for (port = 0; port < nr_port; port++) {
1480                 /* Create FWD stream */
1481                 for (queue = 0; queue < RXQ_NUM; queue++) {
1482                         if (!lcore_infos[lcore].streams_nb ||
1483                                 !(stream_id % lcore_infos[lcore].streams_nb)) {
1484                                 lcore = rte_get_next_lcore(lcore, 0, 0);
1485                                 lcore_infos[lcore].mode = LCORE_MODE_PKT;
1486                                 stream_id = 0;
1487                         }
1488                         lcore_infos[lcore].streams[stream_id].rx_queue = queue;
1489                         lcore_infos[lcore].streams[stream_id].tx_queue = queue;
1490                         lcore_infos[lcore].streams[stream_id].rx_port = port;
1491                         lcore_infos[lcore].streams[stream_id].tx_port = port;
1492                         stream_id++;
1493                 }
1494         }
1495
1496         /* Print all streams */
1497         printf(":: Stream -> core id[N]: (rx_port, rx_queue)->(tx_port, tx_queue)\n");
1498         for (i = 0; i < RTE_MAX_LCORE; i++)
1499                 for (j = 0; j < MAX_STREAMS; j++) {
1500                         /* No streams for this core */
1501                         if (lcore_infos[i].streams[j].tx_port == -1)
1502                                 break;
1503                         printf("Stream -> core id[%d]: (%d,%d)->(%d,%d)\n",
1504                                 i,
1505                                 lcore_infos[i].streams[j].rx_port,
1506                                 lcore_infos[i].streams[j].rx_queue,
1507                                 lcore_infos[i].streams[j].tx_port,
1508                                 lcore_infos[i].streams[j].tx_queue);
1509                 }
1510 }
1511
1512 static void
1513 init_port(void)
1514 {
1515         int ret;
1516         uint16_t std_queue;
1517         uint16_t hairpin_queue;
1518         uint16_t port_id;
1519         uint16_t nr_ports;
1520         uint16_t nr_queues;
1521         struct rte_eth_hairpin_conf hairpin_conf = {
1522                 .peer_count = 1,
1523         };
1524         struct rte_eth_conf port_conf = {
1525                 .rx_adv_conf = {
1526                         .rss_conf.rss_hf =
1527                                 GET_RSS_HF(),
1528                 }
1529         };
1530         struct rte_eth_txconf txq_conf;
1531         struct rte_eth_rxconf rxq_conf;
1532         struct rte_eth_dev_info dev_info;
1533
1534         nr_queues = RXQ_NUM;
1535         if (hairpin_queues_num != 0)
1536                 nr_queues = RXQ_NUM + hairpin_queues_num;
1537
1538         nr_ports = rte_eth_dev_count_avail();
1539         if (nr_ports == 0)
1540                 rte_exit(EXIT_FAILURE, "Error: no port detected\n");
1541
1542         mbuf_mp = rte_pktmbuf_pool_create("mbuf_pool",
1543                                         TOTAL_MBUF_NUM, MBUF_CACHE_SIZE,
1544                                         0, MBUF_SIZE,
1545                                         rte_socket_id());
1546         if (mbuf_mp == NULL)
1547                 rte_exit(EXIT_FAILURE, "Error: can't init mbuf pool\n");
1548
1549         for (port_id = 0; port_id < nr_ports; port_id++) {
1550                 ret = rte_eth_dev_info_get(port_id, &dev_info);
1551                 if (ret != 0)
1552                         rte_exit(EXIT_FAILURE,
1553                                 "Error during getting device"
1554                                 " (port %u) info: %s\n",
1555                                 port_id, strerror(-ret));
1556
1557                 port_conf.txmode.offloads &= dev_info.tx_offload_capa;
1558                 port_conf.rxmode.offloads &= dev_info.rx_offload_capa;
1559
1560                 printf(":: initializing port: %d\n", port_id);
1561
1562                 ret = rte_eth_dev_configure(port_id, nr_queues,
1563                                 nr_queues, &port_conf);
1564                 if (ret < 0)
1565                         rte_exit(EXIT_FAILURE,
1566                                 ":: cannot configure device: err=%d, port=%u\n",
1567                                 ret, port_id);
1568
1569                 rxq_conf = dev_info.default_rxconf;
1570                 for (std_queue = 0; std_queue < RXQ_NUM; std_queue++) {
1571                         ret = rte_eth_rx_queue_setup(port_id, std_queue, NR_RXD,
1572                                         rte_eth_dev_socket_id(port_id),
1573                                         &rxq_conf,
1574                                         mbuf_mp);
1575                         if (ret < 0)
1576                                 rte_exit(EXIT_FAILURE,
1577                                         ":: Rx queue setup failed: err=%d, port=%u\n",
1578                                         ret, port_id);
1579                 }
1580
1581                 txq_conf = dev_info.default_txconf;
1582                 for (std_queue = 0; std_queue < TXQ_NUM; std_queue++) {
1583                         ret = rte_eth_tx_queue_setup(port_id, std_queue, NR_TXD,
1584                                         rte_eth_dev_socket_id(port_id),
1585                                         &txq_conf);
1586                         if (ret < 0)
1587                                 rte_exit(EXIT_FAILURE,
1588                                         ":: Tx queue setup failed: err=%d, port=%u\n",
1589                                         ret, port_id);
1590                 }
1591
1592                 /* Catch all packets from traffic generator. */
1593                 ret = rte_eth_promiscuous_enable(port_id);
1594                 if (ret != 0)
1595                         rte_exit(EXIT_FAILURE,
1596                                 ":: promiscuous mode enable failed: err=%s, port=%u\n",
1597                                 rte_strerror(-ret), port_id);
1598
1599                 if (hairpin_queues_num != 0) {
1600                         /*
1601                          * Configure peer which represents hairpin Tx.
1602                          * Hairpin queue numbers start after standard queues
1603                          * (RXQ_NUM and TXQ_NUM).
1604                          */
1605                         for (hairpin_queue = RXQ_NUM, std_queue = 0;
1606                                         hairpin_queue < nr_queues;
1607                                         hairpin_queue++, std_queue++) {
1608                                 hairpin_conf.peers[0].port = port_id;
1609                                 hairpin_conf.peers[0].queue =
1610                                         std_queue + TXQ_NUM;
1611                                 ret = rte_eth_rx_hairpin_queue_setup(
1612                                                 port_id, hairpin_queue,
1613                                                 NR_RXD, &hairpin_conf);
1614                                 if (ret != 0)
1615                                         rte_exit(EXIT_FAILURE,
1616                                                 ":: Hairpin rx queue setup failed: err=%d, port=%u\n",
1617                                                 ret, port_id);
1618                         }
1619
1620                         for (hairpin_queue = TXQ_NUM, std_queue = 0;
1621                                         hairpin_queue < nr_queues;
1622                                         hairpin_queue++, std_queue++) {
1623                                 hairpin_conf.peers[0].port = port_id;
1624                                 hairpin_conf.peers[0].queue =
1625                                         std_queue + RXQ_NUM;
1626                                 ret = rte_eth_tx_hairpin_queue_setup(
1627                                                 port_id, hairpin_queue,
1628                                                 NR_TXD, &hairpin_conf);
1629                                 if (ret != 0)
1630                                         rte_exit(EXIT_FAILURE,
1631                                                 ":: Hairpin tx queue setup failed: err=%d, port=%u\n",
1632                                                 ret, port_id);
1633                         }
1634                 }
1635
1636                 ret = rte_eth_dev_start(port_id);
1637                 if (ret < 0)
1638                         rte_exit(EXIT_FAILURE,
1639                                 "rte_eth_dev_start:err=%d, port=%u\n",
1640                                 ret, port_id);
1641
1642                 printf(":: initializing port: %d done\n", port_id);
1643         }
1644 }
1645
1646 int
1647 main(int argc, char **argv)
1648 {
1649         int ret;
1650         uint16_t port;
1651         struct rte_flow_error error;
1652
1653         ret = rte_eal_init(argc, argv);
1654         if (ret < 0)
1655                 rte_exit(EXIT_FAILURE, "EAL init failed\n");
1656
1657         force_quit = false;
1658         dump_iterations = false;
1659         rules_count = DEFAULT_RULES_COUNT;
1660         rules_batch = DEFAULT_RULES_BATCH;
1661         delete_flag = false;
1662         dump_socket_mem_flag = false;
1663         flow_group = DEFAULT_GROUP;
1664
1665         signal(SIGINT, signal_handler);
1666         signal(SIGTERM, signal_handler);
1667
1668         argc -= ret;
1669         argv += ret;
1670         if (argc > 1)
1671                 args_parse(argc, argv);
1672
1673         init_port();
1674
1675         nb_lcores = rte_lcore_count();
1676         if (nb_lcores <= 1)
1677                 rte_exit(EXIT_FAILURE, "This app needs at least two cores\n");
1678
1679
1680         printf(":: Flows Count per port: %d\n\n", rules_count);
1681
1682         rte_eal_mp_remote_launch(run_rte_flow_handler_cores, NULL, CALL_MAIN);
1683
1684         if (enable_fwd) {
1685                 init_lcore_info();
1686                 rte_eal_mp_remote_launch(start_forwarding, NULL, CALL_MAIN);
1687         }
1688
1689         RTE_ETH_FOREACH_DEV(port) {
1690                 rte_flow_flush(port, &error);
1691                 if (rte_eth_dev_stop(port) != 0)
1692                         printf("Failed to stop device on port %u\n", port);
1693                 rte_eth_dev_close(port);
1694         }
1695         printf("\nBye ...\n");
1696         return 0;
1697 }