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