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