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