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 params.action[RTE_COLOR_GREEN] = 932 MTR_POLICER_ACTION_COLOR_GREEN; 933 params.action[RTE_COLOR_YELLOW] = 934 MTR_POLICER_ACTION_COLOR_YELLOW; 935 params.action[RTE_COLOR_RED] = 936 MTR_POLICER_ACTION_DROP; 937 938 ret = rte_mtr_create(port_id, counter, ¶ms, 1, &error); 939 if (ret != 0) { 940 printf("Port %u create meter idx(%d) error(%d) message: %s\n", 941 port_id, counter, error.type, 942 error.message ? error.message : "(no stated reason)"); 943 rte_exit(EXIT_FAILURE, "Error in creating meter\n"); 944 } 945 } 946 947 static void 948 destroy_meter_rule(int port_id, uint32_t counter) 949 { 950 struct rte_mtr_error error; 951 952 if (rte_mtr_destroy(port_id, counter, &error)) { 953 printf("Port %u destroy meter(%d) error(%d) message: %s\n", 954 port_id, counter, error.type, 955 error.message ? error.message : "(no stated reason)"); 956 rte_exit(EXIT_FAILURE, "Error in deleting meter rule\n"); 957 } 958 } 959 960 static void 961 meters_handler(int port_id, uint8_t core_id, uint8_t ops) 962 { 963 uint64_t start_batch; 964 double cpu_time_used, insertion_rate; 965 int rules_count_per_core, rules_batch_idx; 966 uint32_t counter, start_counter = 0, end_counter; 967 double cpu_time_per_batch[MAX_BATCHES_COUNT] = { 0 }; 968 969 rules_count_per_core = rules_count / mc_pool.cores_count; 970 971 if (core_id) 972 start_counter = core_id * rules_count_per_core; 973 end_counter = (core_id + 1) * rules_count_per_core; 974 975 cpu_time_used = 0; 976 start_batch = rte_get_timer_cycles(); 977 for (counter = start_counter; counter < end_counter; counter++) { 978 if (ops == METER_CREATE) 979 create_meter_rule(port_id, counter); 980 else 981 destroy_meter_rule(port_id, counter); 982 /* 983 * Save the insertion rate for rules batch. 984 * Check if the insertion reached the rules 985 * patch counter, then save the insertion rate 986 * for this batch. 987 */ 988 if (!((counter + 1) % rules_batch)) { 989 rules_batch_idx = ((counter + 1) / rules_batch) - 1; 990 cpu_time_per_batch[rules_batch_idx] = 991 ((double)(rte_get_timer_cycles() - start_batch)) 992 / rte_get_timer_hz(); 993 cpu_time_used += cpu_time_per_batch[rules_batch_idx]; 994 start_batch = rte_get_timer_cycles(); 995 } 996 } 997 998 /* Print insertion rates for all batches */ 999 if (dump_iterations) 1000 print_rules_batches(cpu_time_per_batch); 1001 1002 insertion_rate = 1003 ((double) (rules_count_per_core / cpu_time_used) / 1000); 1004 1005 /* Insertion rate for all rules in one core */ 1006 printf(":: Port %d :: Core %d Meter %s :: start @[%d] - end @[%d]," 1007 " use:%.02fs, rate:%.02fk Rule/Sec\n", 1008 port_id, core_id, ops == METER_CREATE ? "create" : "delete", 1009 start_counter, end_counter - 1, 1010 cpu_time_used, insertion_rate); 1011 1012 if (ops == METER_CREATE) 1013 mc_pool.meters_record.insertion[port_id][core_id] 1014 = cpu_time_used; 1015 else 1016 mc_pool.meters_record.deletion[port_id][core_id] 1017 = cpu_time_used; 1018 } 1019 1020 static void 1021 destroy_meter_profile(void) 1022 { 1023 struct rte_mtr_error error; 1024 uint16_t nr_ports; 1025 int port_id; 1026 1027 nr_ports = rte_eth_dev_count_avail(); 1028 for (port_id = 0; port_id < nr_ports; port_id++) { 1029 /* If port outside portmask */ 1030 if (!((ports_mask >> port_id) & 0x1)) 1031 continue; 1032 1033 if (rte_mtr_meter_profile_delete 1034 (port_id, DEFAULT_METER_PROF_ID, &error)) { 1035 printf("Port %u del profile error(%d) message: %s\n", 1036 port_id, error.type, 1037 error.message ? error.message : "(no stated reason)"); 1038 rte_exit(EXIT_FAILURE, "Error: Destroy meter profile Failed!\n"); 1039 } 1040 } 1041 } 1042 1043 static void 1044 create_meter_profile(void) 1045 { 1046 uint16_t nr_ports; 1047 int ret, port_id; 1048 struct rte_mtr_meter_profile mp; 1049 struct rte_mtr_error error; 1050 1051 /* 1052 *currently , only create one meter file for one port 1053 *1 meter profile -> N meter rules -> N rte flows 1054 */ 1055 memset(&mp, 0, sizeof(struct rte_mtr_meter_profile)); 1056 nr_ports = rte_eth_dev_count_avail(); 1057 for (port_id = 0; port_id < nr_ports; port_id++) { 1058 /* If port outside portmask */ 1059 if (!((ports_mask >> port_id) & 0x1)) 1060 continue; 1061 1062 mp.alg = RTE_MTR_SRTCM_RFC2697; 1063 mp.srtcm_rfc2697.cir = METER_CIR; 1064 mp.srtcm_rfc2697.cbs = METER_CIR / 8; 1065 mp.srtcm_rfc2697.ebs = 0; 1066 1067 ret = rte_mtr_meter_profile_add 1068 (port_id, DEFAULT_METER_PROF_ID, &mp, &error); 1069 if (ret != 0) { 1070 printf("Port %u create Profile error(%d) message: %s\n", 1071 port_id, error.type, 1072 error.message ? error.message : "(no stated reason)"); 1073 rte_exit(EXIT_FAILURE, "Error: Creation meter profile Failed!\n"); 1074 } 1075 } 1076 } 1077 1078 static inline void 1079 destroy_flows(int port_id, uint8_t core_id, struct rte_flow **flows_list) 1080 { 1081 struct rte_flow_error error; 1082 clock_t start_batch, end_batch; 1083 double cpu_time_used = 0; 1084 double deletion_rate; 1085 double cpu_time_per_batch[MAX_BATCHES_COUNT] = { 0 }; 1086 double delta; 1087 uint32_t i; 1088 int rules_batch_idx; 1089 int rules_count_per_core; 1090 1091 rules_count_per_core = rules_count / mc_pool.cores_count; 1092 /* If group > 0 , should add 1 flow which created in group 0 */ 1093 if (flow_group > 0 && core_id == 0) 1094 rules_count_per_core++; 1095 1096 start_batch = rte_get_timer_cycles(); 1097 for (i = 0; i < (uint32_t) rules_count_per_core; i++) { 1098 if (flows_list[i] == 0) 1099 break; 1100 1101 memset(&error, 0x33, sizeof(error)); 1102 if (rte_flow_destroy(port_id, flows_list[i], &error)) { 1103 print_flow_error(error); 1104 rte_exit(EXIT_FAILURE, "Error in deleting flow\n"); 1105 } 1106 1107 /* 1108 * Save the deletion rate for rules batch. 1109 * Check if the deletion reached the rules 1110 * patch counter, then save the deletion rate 1111 * for this batch. 1112 */ 1113 if (!((i + 1) % rules_batch)) { 1114 end_batch = rte_get_timer_cycles(); 1115 delta = (double) (end_batch - start_batch); 1116 rules_batch_idx = ((i + 1) / rules_batch) - 1; 1117 cpu_time_per_batch[rules_batch_idx] = delta / rte_get_timer_hz(); 1118 cpu_time_used += cpu_time_per_batch[rules_batch_idx]; 1119 start_batch = rte_get_timer_cycles(); 1120 } 1121 } 1122 1123 /* Print deletion rates for all batches */ 1124 if (dump_iterations) 1125 print_rules_batches(cpu_time_per_batch); 1126 1127 /* Deletion rate for all rules */ 1128 deletion_rate = ((double) (rules_count_per_core / cpu_time_used) / 1000); 1129 printf(":: Port %d :: Core %d :: Rules deletion rate -> %f K Rule/Sec\n", 1130 port_id, core_id, deletion_rate); 1131 printf(":: Port %d :: Core %d :: The time for deleting %d rules is %f seconds\n", 1132 port_id, core_id, rules_count_per_core, cpu_time_used); 1133 1134 mc_pool.flows_record.deletion[port_id][core_id] = cpu_time_used; 1135 } 1136 1137 static struct rte_flow ** 1138 insert_flows(int port_id, uint8_t core_id) 1139 { 1140 struct rte_flow **flows_list; 1141 struct rte_flow_error error; 1142 clock_t start_batch, end_batch; 1143 double first_flow_latency; 1144 double cpu_time_used; 1145 double insertion_rate; 1146 double cpu_time_per_batch[MAX_BATCHES_COUNT] = { 0 }; 1147 double delta; 1148 uint32_t flow_index; 1149 uint32_t counter, start_counter = 0, end_counter; 1150 uint64_t global_items[MAX_ITEMS_NUM] = { 0 }; 1151 uint64_t global_actions[MAX_ACTIONS_NUM] = { 0 }; 1152 int rules_batch_idx; 1153 int rules_count_per_core; 1154 1155 rules_count_per_core = rules_count / mc_pool.cores_count; 1156 1157 /* Set boundaries of rules for each core. */ 1158 if (core_id) 1159 start_counter = core_id * rules_count_per_core; 1160 end_counter = (core_id + 1) * rules_count_per_core; 1161 1162 global_items[0] = FLOW_ITEM_MASK(RTE_FLOW_ITEM_TYPE_ETH); 1163 global_actions[0] = FLOW_ITEM_MASK(RTE_FLOW_ACTION_TYPE_JUMP); 1164 1165 flows_list = rte_zmalloc("flows_list", 1166 (sizeof(struct rte_flow *) * rules_count_per_core) + 1, 0); 1167 if (flows_list == NULL) 1168 rte_exit(EXIT_FAILURE, "No Memory available!\n"); 1169 1170 cpu_time_used = 0; 1171 flow_index = 0; 1172 if (flow_group > 0 && core_id == 0) { 1173 /* 1174 * Create global rule to jump into flow_group, 1175 * this way the app will avoid the default rules. 1176 * 1177 * This rule will be created only once. 1178 * 1179 * Global rule: 1180 * group 0 eth / end actions jump group <flow_group> 1181 */ 1182 flow = generate_flow(port_id, 0, flow_attrs, 1183 global_items, global_actions, 1184 flow_group, 0, 0, 0, 0, core_id, unique_data, &error); 1185 1186 if (flow == NULL) { 1187 print_flow_error(error); 1188 rte_exit(EXIT_FAILURE, "Error in creating flow\n"); 1189 } 1190 flows_list[flow_index++] = flow; 1191 } 1192 1193 start_batch = rte_get_timer_cycles(); 1194 for (counter = start_counter; counter < end_counter; counter++) { 1195 flow = generate_flow(port_id, flow_group, 1196 flow_attrs, flow_items, flow_actions, 1197 JUMP_ACTION_TABLE, counter, 1198 hairpin_queues_num, 1199 encap_data, decap_data, 1200 core_id, unique_data, &error); 1201 1202 if (!counter) { 1203 first_flow_latency = (double) (rte_get_timer_cycles() - start_batch); 1204 first_flow_latency /= rte_get_timer_hz(); 1205 /* In millisecond */ 1206 first_flow_latency *= 1000; 1207 printf(":: First Flow Latency :: Port %d :: First flow " 1208 "installed in %f milliseconds\n", 1209 port_id, first_flow_latency); 1210 } 1211 1212 if (force_quit) 1213 counter = end_counter; 1214 1215 if (!flow) { 1216 print_flow_error(error); 1217 rte_exit(EXIT_FAILURE, "Error in creating flow\n"); 1218 } 1219 1220 flows_list[flow_index++] = flow; 1221 1222 /* 1223 * Save the insertion rate for rules batch. 1224 * Check if the insertion reached the rules 1225 * patch counter, then save the insertion rate 1226 * for this batch. 1227 */ 1228 if (!((counter + 1) % rules_batch)) { 1229 end_batch = rte_get_timer_cycles(); 1230 delta = (double) (end_batch - start_batch); 1231 rules_batch_idx = ((counter + 1) / rules_batch) - 1; 1232 cpu_time_per_batch[rules_batch_idx] = delta / rte_get_timer_hz(); 1233 cpu_time_used += cpu_time_per_batch[rules_batch_idx]; 1234 start_batch = rte_get_timer_cycles(); 1235 } 1236 } 1237 1238 /* Print insertion rates for all batches */ 1239 if (dump_iterations) 1240 print_rules_batches(cpu_time_per_batch); 1241 1242 printf(":: Port %d :: Core %d boundaries :: start @[%d] - end @[%d]\n", 1243 port_id, core_id, start_counter, end_counter - 1); 1244 1245 /* Insertion rate for all rules in one core */ 1246 insertion_rate = ((double) (rules_count_per_core / cpu_time_used) / 1000); 1247 printf(":: Port %d :: Core %d :: Rules insertion rate -> %f K Rule/Sec\n", 1248 port_id, core_id, insertion_rate); 1249 printf(":: Port %d :: Core %d :: The time for creating %d in rules %f seconds\n", 1250 port_id, core_id, rules_count_per_core, cpu_time_used); 1251 1252 mc_pool.flows_record.insertion[port_id][core_id] = cpu_time_used; 1253 return flows_list; 1254 } 1255 1256 static void 1257 flows_handler(uint8_t core_id) 1258 { 1259 struct rte_flow **flows_list; 1260 uint16_t nr_ports; 1261 int port_id; 1262 1263 nr_ports = rte_eth_dev_count_avail(); 1264 1265 if (rules_batch > rules_count) 1266 rules_batch = rules_count; 1267 1268 printf(":: Rules Count per port: %d\n\n", rules_count); 1269 1270 for (port_id = 0; port_id < nr_ports; port_id++) { 1271 /* If port outside portmask */ 1272 if (!((ports_mask >> port_id) & 0x1)) 1273 continue; 1274 1275 /* Insertion part. */ 1276 mc_pool.last_alloc[core_id] = (int64_t)dump_socket_mem(stdout); 1277 if (has_meter()) 1278 meters_handler(port_id, core_id, METER_CREATE); 1279 flows_list = insert_flows(port_id, core_id); 1280 if (flows_list == NULL) 1281 rte_exit(EXIT_FAILURE, "Error: Insertion Failed!\n"); 1282 mc_pool.current_alloc[core_id] = (int64_t)dump_socket_mem(stdout); 1283 1284 /* Deletion part. */ 1285 if (delete_flag) { 1286 destroy_flows(port_id, core_id, flows_list); 1287 if (has_meter()) 1288 meters_handler(port_id, core_id, METER_DELETE); 1289 } 1290 } 1291 } 1292 1293 static void 1294 dump_used_cpu_time(const char *item, 1295 uint16_t port, struct used_cpu_time *used_time) 1296 { 1297 uint32_t i; 1298 /* Latency: total count of rte rules divided 1299 * over max time used by thread between all 1300 * threads time. 1301 * 1302 * Throughput: total count of rte rules divided 1303 * over the average of the time cosumed by all 1304 * threads time. 1305 */ 1306 double insertion_latency_time; 1307 double insertion_throughput_time; 1308 double deletion_latency_time; 1309 double deletion_throughput_time; 1310 double insertion_latency, insertion_throughput; 1311 double deletion_latency, deletion_throughput; 1312 1313 /* Save first insertion/deletion rates from first thread. 1314 * Start comparing with all threads, if any thread used 1315 * time more than current saved, replace it. 1316 * 1317 * Thus in the end we will have the max time used for 1318 * insertion/deletion by one thread. 1319 * 1320 * As for memory consumption, save the min of all threads 1321 * of last alloc, and save the max for all threads for 1322 * current alloc. 1323 */ 1324 1325 insertion_latency_time = used_time->insertion[port][0]; 1326 deletion_latency_time = used_time->deletion[port][0]; 1327 insertion_throughput_time = used_time->insertion[port][0]; 1328 deletion_throughput_time = used_time->deletion[port][0]; 1329 1330 i = mc_pool.cores_count; 1331 while (i-- > 1) { 1332 insertion_throughput_time += used_time->insertion[port][i]; 1333 deletion_throughput_time += used_time->deletion[port][i]; 1334 if (insertion_latency_time < used_time->insertion[port][i]) 1335 insertion_latency_time = used_time->insertion[port][i]; 1336 if (deletion_latency_time < used_time->deletion[port][i]) 1337 deletion_latency_time = used_time->deletion[port][i]; 1338 } 1339 1340 insertion_latency = ((double) (mc_pool.rules_count 1341 / insertion_latency_time) / 1000); 1342 deletion_latency = ((double) (mc_pool.rules_count 1343 / deletion_latency_time) / 1000); 1344 1345 insertion_throughput_time /= mc_pool.cores_count; 1346 deletion_throughput_time /= mc_pool.cores_count; 1347 insertion_throughput = ((double) (mc_pool.rules_count 1348 / insertion_throughput_time) / 1000); 1349 deletion_throughput = ((double) (mc_pool.rules_count 1350 / deletion_throughput_time) / 1000); 1351 1352 /* Latency stats */ 1353 printf("\n%s\n:: [Latency | Insertion] All Cores :: Port %d :: ", 1354 item, port); 1355 printf("Total flows insertion rate -> %f K Rules/Sec\n", 1356 insertion_latency); 1357 printf(":: [Latency | Insertion] All Cores :: Port %d :: ", port); 1358 printf("The time for creating %d rules is %f seconds\n", 1359 mc_pool.rules_count, insertion_latency_time); 1360 1361 /* Throughput stats */ 1362 printf(":: [Throughput | Insertion] All Cores :: Port %d :: ", port); 1363 printf("Total flows insertion rate -> %f K Rules/Sec\n", 1364 insertion_throughput); 1365 printf(":: [Throughput | Insertion] All Cores :: Port %d :: ", port); 1366 printf("The average time for creating %d rules is %f seconds\n", 1367 mc_pool.rules_count, insertion_throughput_time); 1368 1369 if (delete_flag) { 1370 /* Latency stats */ 1371 printf(":: [Latency | Deletion] All Cores :: Port %d :: Total " 1372 "deletion rate -> %f K Rules/Sec\n", 1373 port, deletion_latency); 1374 printf(":: [Latency | Deletion] All Cores :: Port %d :: ", 1375 port); 1376 printf("The time for deleting %d rules is %f seconds\n", 1377 mc_pool.rules_count, deletion_latency_time); 1378 1379 /* Throughput stats */ 1380 printf(":: [Throughput | Deletion] All Cores :: Port %d :: Total " 1381 "deletion rate -> %f K Rules/Sec\n", 1382 port, deletion_throughput); 1383 printf(":: [Throughput | Deletion] All Cores :: Port %d :: ", 1384 port); 1385 printf("The average time for deleting %d rules is %f seconds\n", 1386 mc_pool.rules_count, deletion_throughput_time); 1387 } 1388 } 1389 1390 static void 1391 dump_used_mem(uint16_t port) 1392 { 1393 uint32_t i; 1394 int64_t last_alloc, current_alloc; 1395 int flow_size_in_bytes; 1396 1397 last_alloc = mc_pool.last_alloc[0]; 1398 current_alloc = mc_pool.current_alloc[0]; 1399 1400 i = mc_pool.cores_count; 1401 while (i-- > 1) { 1402 if (last_alloc > mc_pool.last_alloc[i]) 1403 last_alloc = mc_pool.last_alloc[i]; 1404 if (current_alloc < mc_pool.current_alloc[i]) 1405 current_alloc = mc_pool.current_alloc[i]; 1406 } 1407 1408 flow_size_in_bytes = (current_alloc - last_alloc) / mc_pool.rules_count; 1409 printf("\n:: Port %d :: rte_flow size in DPDK layer: %d Bytes\n", 1410 port, flow_size_in_bytes); 1411 } 1412 1413 static int 1414 run_rte_flow_handler_cores(void *data __rte_unused) 1415 { 1416 uint16_t port; 1417 int lcore_counter = 0; 1418 int lcore_id = rte_lcore_id(); 1419 int i; 1420 1421 RTE_LCORE_FOREACH(i) { 1422 /* If core not needed return. */ 1423 if (lcore_id == i) { 1424 printf(":: lcore %d mapped with index %d\n", lcore_id, lcore_counter); 1425 if (lcore_counter >= (int) mc_pool.cores_count) 1426 return 0; 1427 break; 1428 } 1429 lcore_counter++; 1430 } 1431 lcore_id = lcore_counter; 1432 1433 if (lcore_id >= (int) mc_pool.cores_count) 1434 return 0; 1435 1436 mc_pool.rules_count = rules_count; 1437 1438 flows_handler(lcore_id); 1439 1440 /* Only main core to print total results. */ 1441 if (lcore_id != 0) 1442 return 0; 1443 1444 /* Make sure all cores finished insertion/deletion process. */ 1445 rte_eal_mp_wait_lcore(); 1446 1447 RTE_ETH_FOREACH_DEV(port) { 1448 /* If port outside portmask */ 1449 if (!((ports_mask >> port) & 0x1)) 1450 continue; 1451 if (has_meter()) 1452 dump_used_cpu_time("Meters:", 1453 port, &mc_pool.meters_record); 1454 dump_used_cpu_time("Flows:", 1455 port, &mc_pool.flows_record); 1456 dump_used_mem(port); 1457 } 1458 1459 return 0; 1460 } 1461 1462 static void 1463 signal_handler(int signum) 1464 { 1465 if (signum == SIGINT || signum == SIGTERM) { 1466 printf("\n\nSignal %d received, preparing to exit...\n", 1467 signum); 1468 printf("Error: Stats are wrong due to sudden signal!\n\n"); 1469 force_quit = true; 1470 } 1471 } 1472 1473 static inline uint16_t 1474 do_rx(struct lcore_info *li, uint16_t rx_port, uint16_t rx_queue) 1475 { 1476 uint16_t cnt = 0; 1477 cnt = rte_eth_rx_burst(rx_port, rx_queue, li->pkts, MAX_PKT_BURST); 1478 li->rx_pkts += cnt; 1479 return cnt; 1480 } 1481 1482 static inline void 1483 do_tx(struct lcore_info *li, uint16_t cnt, uint16_t tx_port, 1484 uint16_t tx_queue) 1485 { 1486 uint16_t nr_tx = 0; 1487 uint16_t i; 1488 1489 nr_tx = rte_eth_tx_burst(tx_port, tx_queue, li->pkts, cnt); 1490 li->tx_pkts += nr_tx; 1491 li->tx_drops += cnt - nr_tx; 1492 1493 for (i = nr_tx; i < cnt; i++) 1494 rte_pktmbuf_free(li->pkts[i]); 1495 } 1496 1497 /* 1498 * Method to convert numbers into pretty numbers that easy 1499 * to read. The design here is to add comma after each three 1500 * digits and set all of this inside buffer. 1501 * 1502 * For example if n = 1799321, the output will be 1503 * 1,799,321 after this method which is easier to read. 1504 */ 1505 static char * 1506 pretty_number(uint64_t n, char *buf) 1507 { 1508 char p[6][4]; 1509 int i = 0; 1510 int off = 0; 1511 1512 while (n > 1000) { 1513 sprintf(p[i], "%03d", (int)(n % 1000)); 1514 n /= 1000; 1515 i += 1; 1516 } 1517 1518 sprintf(p[i++], "%d", (int)n); 1519 1520 while (i--) 1521 off += sprintf(buf + off, "%s,", p[i]); 1522 buf[strlen(buf) - 1] = '\0'; 1523 1524 return buf; 1525 } 1526 1527 static void 1528 packet_per_second_stats(void) 1529 { 1530 struct lcore_info *old; 1531 struct lcore_info *li, *oli; 1532 int nr_lines = 0; 1533 int i; 1534 1535 old = rte_zmalloc("old", 1536 sizeof(struct lcore_info) * RTE_MAX_LCORE, 0); 1537 if (old == NULL) 1538 rte_exit(EXIT_FAILURE, "No Memory available!\n"); 1539 1540 memcpy(old, lcore_infos, 1541 sizeof(struct lcore_info) * RTE_MAX_LCORE); 1542 1543 while (!force_quit) { 1544 uint64_t total_tx_pkts = 0; 1545 uint64_t total_rx_pkts = 0; 1546 uint64_t total_tx_drops = 0; 1547 uint64_t tx_delta, rx_delta, drops_delta; 1548 char buf[3][32]; 1549 int nr_valid_core = 0; 1550 1551 sleep(1); 1552 1553 if (nr_lines) { 1554 char go_up_nr_lines[16]; 1555 1556 sprintf(go_up_nr_lines, "%c[%dA\r", 27, nr_lines); 1557 printf("%s\r", go_up_nr_lines); 1558 } 1559 1560 printf("\n%6s %16s %16s %16s\n", "core", "tx", "tx drops", "rx"); 1561 printf("%6s %16s %16s %16s\n", "------", "----------------", 1562 "----------------", "----------------"); 1563 nr_lines = 3; 1564 for (i = 0; i < RTE_MAX_LCORE; i++) { 1565 li = &lcore_infos[i]; 1566 oli = &old[i]; 1567 if (li->mode != LCORE_MODE_PKT) 1568 continue; 1569 1570 tx_delta = li->tx_pkts - oli->tx_pkts; 1571 rx_delta = li->rx_pkts - oli->rx_pkts; 1572 drops_delta = li->tx_drops - oli->tx_drops; 1573 printf("%6d %16s %16s %16s\n", i, 1574 pretty_number(tx_delta, buf[0]), 1575 pretty_number(drops_delta, buf[1]), 1576 pretty_number(rx_delta, buf[2])); 1577 1578 total_tx_pkts += tx_delta; 1579 total_rx_pkts += rx_delta; 1580 total_tx_drops += drops_delta; 1581 1582 nr_valid_core++; 1583 nr_lines += 1; 1584 } 1585 1586 if (nr_valid_core > 1) { 1587 printf("%6s %16s %16s %16s\n", "total", 1588 pretty_number(total_tx_pkts, buf[0]), 1589 pretty_number(total_tx_drops, buf[1]), 1590 pretty_number(total_rx_pkts, buf[2])); 1591 nr_lines += 1; 1592 } 1593 1594 memcpy(old, lcore_infos, 1595 sizeof(struct lcore_info) * RTE_MAX_LCORE); 1596 } 1597 } 1598 1599 static int 1600 start_forwarding(void *data __rte_unused) 1601 { 1602 int lcore = rte_lcore_id(); 1603 int stream_id; 1604 uint16_t cnt; 1605 struct lcore_info *li = &lcore_infos[lcore]; 1606 1607 if (!li->mode) 1608 return 0; 1609 1610 if (li->mode == LCORE_MODE_STATS) { 1611 printf(":: started stats on lcore %u\n", lcore); 1612 packet_per_second_stats(); 1613 return 0; 1614 } 1615 1616 while (!force_quit) 1617 for (stream_id = 0; stream_id < MAX_STREAMS; stream_id++) { 1618 if (li->streams[stream_id].rx_port == -1) 1619 continue; 1620 1621 cnt = do_rx(li, 1622 li->streams[stream_id].rx_port, 1623 li->streams[stream_id].rx_queue); 1624 if (cnt) 1625 do_tx(li, cnt, 1626 li->streams[stream_id].tx_port, 1627 li->streams[stream_id].tx_queue); 1628 } 1629 return 0; 1630 } 1631 1632 static void 1633 init_lcore_info(void) 1634 { 1635 int i, j; 1636 unsigned int lcore; 1637 uint16_t nr_port; 1638 uint16_t queue; 1639 int port; 1640 int stream_id = 0; 1641 int streams_per_core; 1642 int unassigned_streams; 1643 int nb_fwd_streams; 1644 nr_port = rte_eth_dev_count_avail(); 1645 1646 /* First logical core is reserved for stats printing */ 1647 lcore = rte_get_next_lcore(-1, 0, 0); 1648 lcore_infos[lcore].mode = LCORE_MODE_STATS; 1649 1650 /* 1651 * Initialize all cores 1652 * All cores at first must have -1 value in all streams 1653 * This means that this stream is not used, or not set 1654 * yet. 1655 */ 1656 for (i = 0; i < RTE_MAX_LCORE; i++) 1657 for (j = 0; j < MAX_STREAMS; j++) { 1658 lcore_infos[i].streams[j].tx_port = -1; 1659 lcore_infos[i].streams[j].rx_port = -1; 1660 lcore_infos[i].streams[j].tx_queue = -1; 1661 lcore_infos[i].streams[j].rx_queue = -1; 1662 lcore_infos[i].streams_nb = 0; 1663 } 1664 1665 /* 1666 * Calculate the total streams count. 1667 * Also distribute those streams count between the available 1668 * logical cores except first core, since it's reserved for 1669 * stats prints. 1670 */ 1671 nb_fwd_streams = nr_port * RXQ_NUM; 1672 if ((int)(nb_lcores - 1) >= nb_fwd_streams) 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 = 1; 1676 } 1677 else { 1678 streams_per_core = nb_fwd_streams / (nb_lcores - 1); 1679 unassigned_streams = nb_fwd_streams % (nb_lcores - 1); 1680 for (i = 0; i < (int)(nb_lcores - 1); i++) { 1681 lcore = rte_get_next_lcore(lcore, 0, 0); 1682 lcore_infos[lcore].streams_nb = streams_per_core; 1683 if (unassigned_streams) { 1684 lcore_infos[lcore].streams_nb++; 1685 unassigned_streams--; 1686 } 1687 } 1688 } 1689 1690 /* 1691 * Set the streams for the cores according to each logical 1692 * core stream count. 1693 * The streams is built on the design of what received should 1694 * forward as well, this means that if you received packets on 1695 * port 0 queue 0 then the same queue should forward the 1696 * packets, using the same logical core. 1697 */ 1698 lcore = rte_get_next_lcore(-1, 0, 0); 1699 for (port = 0; port < nr_port; port++) { 1700 /* Create FWD stream */ 1701 for (queue = 0; queue < RXQ_NUM; queue++) { 1702 if (!lcore_infos[lcore].streams_nb || 1703 !(stream_id % lcore_infos[lcore].streams_nb)) { 1704 lcore = rte_get_next_lcore(lcore, 0, 0); 1705 lcore_infos[lcore].mode = LCORE_MODE_PKT; 1706 stream_id = 0; 1707 } 1708 lcore_infos[lcore].streams[stream_id].rx_queue = queue; 1709 lcore_infos[lcore].streams[stream_id].tx_queue = queue; 1710 lcore_infos[lcore].streams[stream_id].rx_port = port; 1711 lcore_infos[lcore].streams[stream_id].tx_port = port; 1712 stream_id++; 1713 } 1714 } 1715 1716 /* Print all streams */ 1717 printf(":: Stream -> core id[N]: (rx_port, rx_queue)->(tx_port, tx_queue)\n"); 1718 for (i = 0; i < RTE_MAX_LCORE; i++) 1719 for (j = 0; j < MAX_STREAMS; j++) { 1720 /* No streams for this core */ 1721 if (lcore_infos[i].streams[j].tx_port == -1) 1722 break; 1723 printf("Stream -> core id[%d]: (%d,%d)->(%d,%d)\n", 1724 i, 1725 lcore_infos[i].streams[j].rx_port, 1726 lcore_infos[i].streams[j].rx_queue, 1727 lcore_infos[i].streams[j].tx_port, 1728 lcore_infos[i].streams[j].tx_queue); 1729 } 1730 } 1731 1732 static void 1733 init_port(void) 1734 { 1735 int ret; 1736 uint16_t std_queue; 1737 uint16_t hairpin_queue; 1738 uint16_t port_id; 1739 uint16_t nr_ports; 1740 uint16_t nr_queues; 1741 struct rte_eth_hairpin_conf hairpin_conf = { 1742 .peer_count = 1, 1743 }; 1744 struct rte_eth_conf port_conf = { 1745 .rx_adv_conf = { 1746 .rss_conf.rss_hf = 1747 GET_RSS_HF(), 1748 } 1749 }; 1750 struct rte_eth_txconf txq_conf; 1751 struct rte_eth_rxconf rxq_conf; 1752 struct rte_eth_dev_info dev_info; 1753 1754 nr_queues = RXQ_NUM; 1755 if (hairpin_queues_num != 0) 1756 nr_queues = RXQ_NUM + hairpin_queues_num; 1757 1758 nr_ports = rte_eth_dev_count_avail(); 1759 if (nr_ports == 0) 1760 rte_exit(EXIT_FAILURE, "Error: no port detected\n"); 1761 1762 mbuf_mp = rte_pktmbuf_pool_create("mbuf_pool", 1763 TOTAL_MBUF_NUM, MBUF_CACHE_SIZE, 1764 0, MBUF_SIZE, 1765 rte_socket_id()); 1766 if (mbuf_mp == NULL) 1767 rte_exit(EXIT_FAILURE, "Error: can't init mbuf pool\n"); 1768 1769 for (port_id = 0; port_id < nr_ports; port_id++) { 1770 ret = rte_eth_dev_info_get(port_id, &dev_info); 1771 if (ret != 0) 1772 rte_exit(EXIT_FAILURE, 1773 "Error during getting device" 1774 " (port %u) info: %s\n", 1775 port_id, strerror(-ret)); 1776 1777 port_conf.txmode.offloads &= dev_info.tx_offload_capa; 1778 port_conf.rxmode.offloads &= dev_info.rx_offload_capa; 1779 1780 printf(":: initializing port: %d\n", port_id); 1781 1782 ret = rte_eth_dev_configure(port_id, nr_queues, 1783 nr_queues, &port_conf); 1784 if (ret < 0) 1785 rte_exit(EXIT_FAILURE, 1786 ":: cannot configure device: err=%d, port=%u\n", 1787 ret, port_id); 1788 1789 rxq_conf = dev_info.default_rxconf; 1790 for (std_queue = 0; std_queue < RXQ_NUM; std_queue++) { 1791 ret = rte_eth_rx_queue_setup(port_id, std_queue, NR_RXD, 1792 rte_eth_dev_socket_id(port_id), 1793 &rxq_conf, 1794 mbuf_mp); 1795 if (ret < 0) 1796 rte_exit(EXIT_FAILURE, 1797 ":: Rx queue setup failed: err=%d, port=%u\n", 1798 ret, port_id); 1799 } 1800 1801 txq_conf = dev_info.default_txconf; 1802 for (std_queue = 0; std_queue < TXQ_NUM; std_queue++) { 1803 ret = rte_eth_tx_queue_setup(port_id, std_queue, NR_TXD, 1804 rte_eth_dev_socket_id(port_id), 1805 &txq_conf); 1806 if (ret < 0) 1807 rte_exit(EXIT_FAILURE, 1808 ":: Tx queue setup failed: err=%d, port=%u\n", 1809 ret, port_id); 1810 } 1811 1812 /* Catch all packets from traffic generator. */ 1813 ret = rte_eth_promiscuous_enable(port_id); 1814 if (ret != 0) 1815 rte_exit(EXIT_FAILURE, 1816 ":: promiscuous mode enable failed: err=%s, port=%u\n", 1817 rte_strerror(-ret), port_id); 1818 1819 if (hairpin_queues_num != 0) { 1820 /* 1821 * Configure peer which represents hairpin Tx. 1822 * Hairpin queue numbers start after standard queues 1823 * (RXQ_NUM and TXQ_NUM). 1824 */ 1825 for (hairpin_queue = RXQ_NUM, std_queue = 0; 1826 hairpin_queue < nr_queues; 1827 hairpin_queue++, std_queue++) { 1828 hairpin_conf.peers[0].port = port_id; 1829 hairpin_conf.peers[0].queue = 1830 std_queue + TXQ_NUM; 1831 ret = rte_eth_rx_hairpin_queue_setup( 1832 port_id, hairpin_queue, 1833 NR_RXD, &hairpin_conf); 1834 if (ret != 0) 1835 rte_exit(EXIT_FAILURE, 1836 ":: Hairpin rx queue setup failed: err=%d, port=%u\n", 1837 ret, port_id); 1838 } 1839 1840 for (hairpin_queue = TXQ_NUM, std_queue = 0; 1841 hairpin_queue < nr_queues; 1842 hairpin_queue++, std_queue++) { 1843 hairpin_conf.peers[0].port = port_id; 1844 hairpin_conf.peers[0].queue = 1845 std_queue + RXQ_NUM; 1846 ret = rte_eth_tx_hairpin_queue_setup( 1847 port_id, hairpin_queue, 1848 NR_TXD, &hairpin_conf); 1849 if (ret != 0) 1850 rte_exit(EXIT_FAILURE, 1851 ":: Hairpin tx queue setup failed: err=%d, port=%u\n", 1852 ret, port_id); 1853 } 1854 } 1855 1856 ret = rte_eth_dev_start(port_id); 1857 if (ret < 0) 1858 rte_exit(EXIT_FAILURE, 1859 "rte_eth_dev_start:err=%d, port=%u\n", 1860 ret, port_id); 1861 1862 printf(":: initializing port: %d done\n", port_id); 1863 } 1864 } 1865 1866 int 1867 main(int argc, char **argv) 1868 { 1869 int ret; 1870 uint16_t port; 1871 struct rte_flow_error error; 1872 1873 ret = rte_eal_init(argc, argv); 1874 if (ret < 0) 1875 rte_exit(EXIT_FAILURE, "EAL init failed\n"); 1876 1877 force_quit = false; 1878 dump_iterations = false; 1879 rules_count = DEFAULT_RULES_COUNT; 1880 rules_batch = DEFAULT_RULES_BATCH; 1881 delete_flag = false; 1882 dump_socket_mem_flag = false; 1883 flow_group = DEFAULT_GROUP; 1884 unique_data = false; 1885 1886 signal(SIGINT, signal_handler); 1887 signal(SIGTERM, signal_handler); 1888 1889 argc -= ret; 1890 argv += ret; 1891 if (argc > 1) 1892 args_parse(argc, argv); 1893 1894 init_port(); 1895 1896 nb_lcores = rte_lcore_count(); 1897 if (nb_lcores <= 1) 1898 rte_exit(EXIT_FAILURE, "This app needs at least two cores\n"); 1899 1900 printf(":: Flows Count per port: %d\n\n", rules_count); 1901 1902 if (has_meter()) 1903 create_meter_profile(); 1904 rte_eal_mp_remote_launch(run_rte_flow_handler_cores, NULL, CALL_MAIN); 1905 1906 if (enable_fwd) { 1907 init_lcore_info(); 1908 rte_eal_mp_remote_launch(start_forwarding, NULL, CALL_MAIN); 1909 } 1910 if (has_meter() && delete_flag) 1911 destroy_meter_profile(); 1912 1913 RTE_ETH_FOREACH_DEV(port) { 1914 rte_flow_flush(port, &error); 1915 if (rte_eth_dev_stop(port) != 0) 1916 printf("Failed to stop device on port %u\n", port); 1917 rte_eth_dev_close(port); 1918 } 1919 printf("\nBye ...\n"); 1920 return 0; 1921 } 1922