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