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