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