dpdk/examples/l2fwd-jobstats/main.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: BSD-3-Clause
   2 * Copyright(c) 2010-2016 Intel Corporation
   3 */
   4
   5#include <locale.h>
   6#include <stdlib.h>
   7#include <string.h>
   8#include <stdint.h>
   9#include <ctype.h>
  10#include <getopt.h>
  11
  12#include <rte_common.h>
  13#include <rte_log.h>
  14#include <rte_malloc.h>
  15#include <rte_memory.h>
  16#include <rte_memcpy.h>
  17#include <rte_eal.h>
  18#include <rte_launch.h>
  19#include <rte_atomic.h>
  20#include <rte_cycles.h>
  21#include <rte_prefetch.h>
  22#include <rte_lcore.h>
  23#include <rte_per_lcore.h>
  24#include <rte_branch_prediction.h>
  25#include <rte_interrupts.h>
  26#include <rte_debug.h>
  27#include <rte_ether.h>
  28#include <rte_ethdev.h>
  29#include <rte_mempool.h>
  30#include <rte_mbuf.h>
  31#include <rte_spinlock.h>
  32
  33#include <rte_errno.h>
  34#include <rte_jobstats.h>
  35#include <rte_timer.h>
  36#include <rte_alarm.h>
  37#include <rte_pause.h>
  38
  39#define RTE_LOGTYPE_L2FWD RTE_LOGTYPE_USER1
  40
  41#define NB_MBUF   8192
  42
  43#define MAX_PKT_BURST 32
  44#define BURST_TX_DRAIN_US 100 /* TX drain every ~100us */
  45
  46/*
  47 * Configurable number of RX/TX ring descriptors
  48 */
  49#define RTE_TEST_RX_DESC_DEFAULT 1024
  50#define RTE_TEST_TX_DESC_DEFAULT 1024
  51static uint16_t nb_rxd = RTE_TEST_RX_DESC_DEFAULT;
  52static uint16_t nb_txd = RTE_TEST_TX_DESC_DEFAULT;
  53
  54/* ethernet addresses of ports */
  55static struct rte_ether_addr l2fwd_ports_eth_addr[RTE_MAX_ETHPORTS];
  56
  57/* mask of enabled ports */
  58static uint32_t l2fwd_enabled_port_mask;
  59
  60/* list of enabled ports */
  61static uint32_t l2fwd_dst_ports[RTE_MAX_ETHPORTS];
  62
  63#define UPDATE_STEP_UP 1
  64#define UPDATE_STEP_DOWN 32
  65
  66static unsigned int l2fwd_rx_queue_per_lcore = 1;
  67
  68#define MAX_RX_QUEUE_PER_LCORE 16
  69#define MAX_TX_QUEUE_PER_PORT 16
  70struct lcore_queue_conf {
  71        unsigned n_rx_port;
  72        unsigned rx_port_list[MAX_RX_QUEUE_PER_LCORE];
  73        uint64_t next_flush_time[RTE_MAX_ETHPORTS];
  74
  75        struct rte_timer rx_timers[MAX_RX_QUEUE_PER_LCORE];
  76        struct rte_jobstats port_fwd_jobs[MAX_RX_QUEUE_PER_LCORE];
  77
  78        struct rte_timer flush_timer;
  79        struct rte_jobstats flush_job;
  80        struct rte_jobstats idle_job;
  81        struct rte_jobstats_context jobs_context;
  82
  83        rte_atomic16_t stats_read_pending;
  84        rte_spinlock_t lock;
  85} __rte_cache_aligned;
  86struct lcore_queue_conf lcore_queue_conf[RTE_MAX_LCORE];
  87
  88struct rte_eth_dev_tx_buffer *tx_buffer[RTE_MAX_ETHPORTS];
  89
  90static struct rte_eth_conf port_conf = {
  91        .rxmode = {
  92                .split_hdr_size = 0,
  93        },
  94        .txmode = {
  95                .mq_mode = ETH_MQ_TX_NONE,
  96        },
  97};
  98
  99struct rte_mempool *l2fwd_pktmbuf_pool = NULL;
 100
 101/* Per-port statistics struct */
 102struct l2fwd_port_statistics {
 103        uint64_t tx;
 104        uint64_t rx;
 105        uint64_t dropped;
 106} __rte_cache_aligned;
 107struct l2fwd_port_statistics port_statistics[RTE_MAX_ETHPORTS];
 108
 109/* 1 day max */
 110#define MAX_TIMER_PERIOD 86400
 111/* default period is 10 seconds */
 112static int64_t timer_period = 10;
 113/* default timer frequency */
 114static double hz;
 115/* BURST_TX_DRAIN_US converted to cycles */
 116uint64_t drain_tsc;
 117/* Convert cycles to ns */
 118static inline double
 119cycles_to_ns(uint64_t cycles)
 120{
 121        double t = cycles;
 122
 123        t *= (double)NS_PER_S;
 124        t /= hz;
 125        return t;
 126}
 127
 128static void
 129show_lcore_stats(unsigned lcore_id)
 130{
 131        struct lcore_queue_conf *qconf = &lcore_queue_conf[lcore_id];
 132        struct rte_jobstats_context *ctx = &qconf->jobs_context;
 133        struct rte_jobstats *job;
 134        uint8_t i;
 135
 136        /* LCore statistics. */
 137        uint64_t stats_period, loop_count;
 138        uint64_t exec, exec_min, exec_max;
 139        uint64_t management, management_min, management_max;
 140        uint64_t busy, busy_min, busy_max;
 141
 142        /* Jobs statistics. */
 143        const uint16_t port_cnt = qconf->n_rx_port;
 144        uint64_t jobs_exec_cnt[port_cnt], jobs_period[port_cnt];
 145        uint64_t jobs_exec[port_cnt], jobs_exec_min[port_cnt],
 146                                jobs_exec_max[port_cnt];
 147
 148        uint64_t flush_exec_cnt, flush_period;
 149        uint64_t flush_exec, flush_exec_min, flush_exec_max;
 150
 151        uint64_t idle_exec_cnt;
 152        uint64_t idle_exec, idle_exec_min, idle_exec_max;
 153        uint64_t collection_time = rte_get_timer_cycles();
 154
 155        /* Ask forwarding thread to give us stats. */
 156        rte_atomic16_set(&qconf->stats_read_pending, 1);
 157        rte_spinlock_lock(&qconf->lock);
 158        rte_atomic16_set(&qconf->stats_read_pending, 0);
 159
 160        /* Collect context statistics. */
 161        stats_period = ctx->state_time - ctx->start_time;
 162        loop_count = ctx->loop_cnt;
 163
 164        exec = ctx->exec_time;
 165        exec_min = ctx->min_exec_time;
 166        exec_max = ctx->max_exec_time;
 167
 168        management = ctx->management_time;
 169        management_min = ctx->min_management_time;
 170        management_max = ctx->max_management_time;
 171
 172        rte_jobstats_context_reset(ctx);
 173
 174        for (i = 0; i < port_cnt; i++) {
 175                job = &qconf->port_fwd_jobs[i];
 176
 177                jobs_exec_cnt[i] = job->exec_cnt;
 178                jobs_period[i] = job->period;
 179
 180                jobs_exec[i] = job->exec_time;
 181                jobs_exec_min[i] = job->min_exec_time;
 182                jobs_exec_max[i] = job->max_exec_time;
 183
 184                rte_jobstats_reset(job);
 185        }
 186
 187        flush_exec_cnt = qconf->flush_job.exec_cnt;
 188        flush_period = qconf->flush_job.period;
 189        flush_exec = qconf->flush_job.exec_time;
 190        flush_exec_min = qconf->flush_job.min_exec_time;
 191        flush_exec_max = qconf->flush_job.max_exec_time;
 192        rte_jobstats_reset(&qconf->flush_job);
 193
 194        idle_exec_cnt = qconf->idle_job.exec_cnt;
 195        idle_exec = qconf->idle_job.exec_time;
 196        idle_exec_min = qconf->idle_job.min_exec_time;
 197        idle_exec_max = qconf->idle_job.max_exec_time;
 198        rte_jobstats_reset(&qconf->idle_job);
 199
 200        rte_spinlock_unlock(&qconf->lock);
 201
 202        exec -= idle_exec;
 203        busy = exec + management;
 204        busy_min = exec_min + management_min;
 205        busy_max = exec_max + management_max;
 206
 207
 208        collection_time = rte_get_timer_cycles() - collection_time;
 209
 210#define STAT_FMT "\n%-18s %'14.0f %6.1f%% %'10.0f %'10.0f %'10.0f"
 211
 212        printf("\n----------------"
 213                        "\nLCore %3u: statistics (time in ns, collected in %'9.0f)"
 214                        "\n%-18s %14s %7s %10s %10s %10s "
 215                        "\n%-18s %'14.0f"
 216                        "\n%-18s %'14" PRIu64
 217                        STAT_FMT /* Exec */
 218                        STAT_FMT /* Management */
 219                        STAT_FMT /* Busy */
 220                        STAT_FMT, /* Idle  */
 221                        lcore_id, cycles_to_ns(collection_time),
 222                        "Stat type", "total", "%total", "avg", "min", "max",
 223                        "Stats duration:", cycles_to_ns(stats_period),
 224                        "Loop count:", loop_count,
 225                        "Exec time",
 226                        cycles_to_ns(exec), exec * 100.0 / stats_period,
 227                        cycles_to_ns(loop_count  ? exec / loop_count : 0),
 228                        cycles_to_ns(exec_min),
 229                        cycles_to_ns(exec_max),
 230                        "Management time",
 231                        cycles_to_ns(management), management * 100.0 / stats_period,
 232                        cycles_to_ns(loop_count  ? management / loop_count : 0),
 233                        cycles_to_ns(management_min),
 234                        cycles_to_ns(management_max),
 235                        "Exec + management",
 236                        cycles_to_ns(busy),  busy * 100.0 / stats_period,
 237                        cycles_to_ns(loop_count ? busy / loop_count : 0),
 238                        cycles_to_ns(busy_min),
 239                        cycles_to_ns(busy_max),
 240                        "Idle (job)",
 241                        cycles_to_ns(idle_exec), idle_exec * 100.0 / stats_period,
 242                        cycles_to_ns(idle_exec_cnt ? idle_exec / idle_exec_cnt : 0),
 243                        cycles_to_ns(idle_exec_min),
 244                        cycles_to_ns(idle_exec_max));
 245
 246        for (i = 0; i < qconf->n_rx_port; i++) {
 247                job = &qconf->port_fwd_jobs[i];
 248                printf("\n\nJob %" PRIu32 ": %-20s "
 249                                "\n%-18s %'14" PRIu64
 250                                "\n%-18s %'14.0f"
 251                                STAT_FMT,
 252                                i, job->name,
 253                                "Exec count:", jobs_exec_cnt[i],
 254                                "Exec period: ", cycles_to_ns(jobs_period[i]),
 255                                "Exec time",
 256                                cycles_to_ns(jobs_exec[i]), jobs_exec[i] * 100.0 / stats_period,
 257                                cycles_to_ns(jobs_exec_cnt[i] ? jobs_exec[i] / jobs_exec_cnt[i]
 258                                                : 0),
 259                                cycles_to_ns(jobs_exec_min[i]),
 260                                cycles_to_ns(jobs_exec_max[i]));
 261        }
 262
 263        if (qconf->n_rx_port > 0) {
 264                job = &qconf->flush_job;
 265                printf("\n\nJob %" PRIu32 ": %-20s "
 266                                "\n%-18s %'14" PRIu64
 267                                "\n%-18s %'14.0f"
 268                                STAT_FMT,
 269                                i, job->name,
 270                                "Exec count:", flush_exec_cnt,
 271                                "Exec period: ", cycles_to_ns(flush_period),
 272                                "Exec time",
 273                                cycles_to_ns(flush_exec), flush_exec * 100.0 / stats_period,
 274                                cycles_to_ns(flush_exec_cnt ? flush_exec / flush_exec_cnt : 0),
 275                                cycles_to_ns(flush_exec_min),
 276                                cycles_to_ns(flush_exec_max));
 277        }
 278}
 279
 280/* Print out statistics on packets dropped */
 281static void
 282show_stats_cb(__rte_unused void *param)
 283{
 284        uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
 285        unsigned portid, lcore_id;
 286
 287        total_packets_dropped = 0;
 288        total_packets_tx = 0;
 289        total_packets_rx = 0;
 290
 291        const char clr[] = { 27, '[', '2', 'J', '\0' };
 292        const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' };
 293
 294        /* Clear screen and move to top left */
 295        printf("%s%s"
 296                        "\nPort statistics ===================================",
 297                        clr, topLeft);
 298
 299        for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) {
 300                /* skip disabled ports */
 301                if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
 302                        continue;
 303                printf("\nStatistics for port %u ------------------------------"
 304                                "\nPackets sent: %24"PRIu64
 305                                "\nPackets received: %20"PRIu64
 306                                "\nPackets dropped: %21"PRIu64,
 307                                portid,
 308                                port_statistics[portid].tx,
 309                                port_statistics[portid].rx,
 310                                port_statistics[portid].dropped);
 311
 312                total_packets_dropped += port_statistics[portid].dropped;
 313                total_packets_tx += port_statistics[portid].tx;
 314                total_packets_rx += port_statistics[portid].rx;
 315        }
 316
 317        printf("\nAggregate statistics ==============================="
 318                        "\nTotal packets sent: %18"PRIu64
 319                        "\nTotal packets received: %14"PRIu64
 320                        "\nTotal packets dropped: %15"PRIu64
 321                        "\n====================================================",
 322                        total_packets_tx,
 323                        total_packets_rx,
 324                        total_packets_dropped);
 325
 326        RTE_LCORE_FOREACH(lcore_id) {
 327                if (lcore_queue_conf[lcore_id].n_rx_port > 0)
 328                        show_lcore_stats(lcore_id);
 329        }
 330
 331        printf("\n====================================================\n");
 332
 333        fflush(stdout);
 334
 335        rte_eal_alarm_set(timer_period * US_PER_S, show_stats_cb, NULL);
 336}
 337
 338static void
 339l2fwd_simple_forward(struct rte_mbuf *m, unsigned portid)
 340{
 341        struct rte_ether_hdr *eth;
 342        void *tmp;
 343        int sent;
 344        unsigned dst_port;
 345        struct rte_eth_dev_tx_buffer *buffer;
 346
 347        dst_port = l2fwd_dst_ports[portid];
 348        eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *);
 349
 350        /* 02:00:00:00:00:xx */
 351        tmp = &eth->d_addr.addr_bytes[0];
 352        *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
 353
 354        /* src addr */
 355        rte_ether_addr_copy(&l2fwd_ports_eth_addr[dst_port], &eth->s_addr);
 356
 357        buffer = tx_buffer[dst_port];
 358        sent = rte_eth_tx_buffer(dst_port, 0, buffer, m);
 359        if (sent)
 360                port_statistics[dst_port].tx += sent;
 361}
 362
 363static void
 364l2fwd_job_update_cb(struct rte_jobstats *job, int64_t result)
 365{
 366        int64_t err = job->target - result;
 367        int64_t histeresis = job->target / 8;
 368
 369        if (err < -histeresis) {
 370                if (job->min_period + UPDATE_STEP_DOWN < job->period)
 371                        job->period -= UPDATE_STEP_DOWN;
 372        } else if (err > histeresis) {
 373                if (job->period + UPDATE_STEP_UP < job->max_period)
 374                        job->period += UPDATE_STEP_UP;
 375        }
 376}
 377
 378static void
 379l2fwd_fwd_job(__rte_unused struct rte_timer *timer, void *arg)
 380{
 381        struct rte_mbuf *pkts_burst[MAX_PKT_BURST];
 382        struct rte_mbuf *m;
 383
 384        const uint16_t port_idx = (uintptr_t) arg;
 385        const unsigned lcore_id = rte_lcore_id();
 386        struct lcore_queue_conf *qconf = &lcore_queue_conf[lcore_id];
 387        struct rte_jobstats *job = &qconf->port_fwd_jobs[port_idx];
 388        const uint16_t portid = qconf->rx_port_list[port_idx];
 389
 390        uint8_t j;
 391        uint16_t total_nb_rx;
 392
 393        rte_jobstats_start(&qconf->jobs_context, job);
 394
 395        /* Call rx burst 2 times. This allow rte_jobstats logic to see if this
 396         * function must be called more frequently. */
 397
 398        total_nb_rx = rte_eth_rx_burst(portid, 0, pkts_burst,
 399                        MAX_PKT_BURST);
 400
 401        for (j = 0; j < total_nb_rx; j++) {
 402                m = pkts_burst[j];
 403                rte_prefetch0(rte_pktmbuf_mtod(m, void *));
 404                l2fwd_simple_forward(m, portid);
 405        }
 406
 407        if (total_nb_rx == MAX_PKT_BURST) {
 408                const uint16_t nb_rx = rte_eth_rx_burst(portid, 0, pkts_burst,
 409                                MAX_PKT_BURST);
 410
 411                total_nb_rx += nb_rx;
 412                for (j = 0; j < nb_rx; j++) {
 413                        m = pkts_burst[j];
 414                        rte_prefetch0(rte_pktmbuf_mtod(m, void *));
 415                        l2fwd_simple_forward(m, portid);
 416                }
 417        }
 418
 419        port_statistics[portid].rx += total_nb_rx;
 420
 421        /* Adjust period time in which we are running here. */
 422        if (rte_jobstats_finish(job, total_nb_rx) != 0) {
 423                rte_timer_reset(&qconf->rx_timers[port_idx], job->period, PERIODICAL,
 424                                lcore_id, l2fwd_fwd_job, arg);
 425        }
 426}
 427
 428static void
 429l2fwd_flush_job(__rte_unused struct rte_timer *timer, __rte_unused void *arg)
 430{
 431        uint64_t now;
 432        unsigned lcore_id;
 433        struct lcore_queue_conf *qconf;
 434        uint16_t portid;
 435        unsigned i;
 436        uint32_t sent;
 437        struct rte_eth_dev_tx_buffer *buffer;
 438
 439        lcore_id = rte_lcore_id();
 440        qconf = &lcore_queue_conf[lcore_id];
 441
 442        rte_jobstats_start(&qconf->jobs_context, &qconf->flush_job);
 443
 444        now = rte_get_timer_cycles();
 445        lcore_id = rte_lcore_id();
 446        qconf = &lcore_queue_conf[lcore_id];
 447
 448        for (i = 0; i < qconf->n_rx_port; i++) {
 449                portid = l2fwd_dst_ports[qconf->rx_port_list[i]];
 450
 451                if (qconf->next_flush_time[portid] <= now)
 452                        continue;
 453
 454                buffer = tx_buffer[portid];
 455                sent = rte_eth_tx_buffer_flush(portid, 0, buffer);
 456                if (sent)
 457                        port_statistics[portid].tx += sent;
 458
 459                qconf->next_flush_time[portid] = rte_get_timer_cycles() + drain_tsc;
 460        }
 461
 462        /* Pass target to indicate that this job is happy of time interwal
 463         * in which it was called. */
 464        rte_jobstats_finish(&qconf->flush_job, qconf->flush_job.target);
 465}
 466
 467/* main processing loop */
 468static void
 469l2fwd_main_loop(void)
 470{
 471        unsigned lcore_id;
 472        unsigned i, portid;
 473        struct lcore_queue_conf *qconf;
 474        uint8_t stats_read_pending = 0;
 475        uint8_t need_manage;
 476
 477        lcore_id = rte_lcore_id();
 478        qconf = &lcore_queue_conf[lcore_id];
 479
 480        if (qconf->n_rx_port == 0) {
 481                RTE_LOG(INFO, L2FWD, "lcore %u has nothing to do\n", lcore_id);
 482                return;
 483        }
 484
 485        RTE_LOG(INFO, L2FWD, "entering main loop on lcore %u\n", lcore_id);
 486
 487        for (i = 0; i < qconf->n_rx_port; i++) {
 488
 489                portid = qconf->rx_port_list[i];
 490                RTE_LOG(INFO, L2FWD, " -- lcoreid=%u portid=%u\n", lcore_id,
 491                        portid);
 492        }
 493
 494        rte_jobstats_init(&qconf->idle_job, "idle", 0, 0, 0, 0);
 495
 496        for (;;) {
 497                rte_spinlock_lock(&qconf->lock);
 498
 499                do {
 500                        rte_jobstats_context_start(&qconf->jobs_context);
 501
 502                        /* Do the Idle job:
 503                         * - Read stats_read_pending flag
 504                         * - check if some real job need to be executed
 505                         */
 506                        rte_jobstats_start(&qconf->jobs_context, &qconf->idle_job);
 507
 508                        uint64_t repeats = 0;
 509
 510                        do {
 511                                uint8_t i;
 512                                uint64_t now = rte_get_timer_cycles();
 513
 514                                repeats++;
 515                                need_manage = qconf->flush_timer.expire < now;
 516                                /* Check if we was esked to give a stats. */
 517                                stats_read_pending =
 518                                                rte_atomic16_read(&qconf->stats_read_pending);
 519                                need_manage |= stats_read_pending;
 520
 521                                for (i = 0; i < qconf->n_rx_port && !need_manage; i++)
 522                                        need_manage = qconf->rx_timers[i].expire < now;
 523
 524                        } while (!need_manage);
 525
 526                        if (likely(repeats != 1))
 527                                rte_jobstats_finish(&qconf->idle_job, qconf->idle_job.target);
 528                        else
 529                                rte_jobstats_abort(&qconf->idle_job);
 530
 531                        rte_timer_manage();
 532                        rte_jobstats_context_finish(&qconf->jobs_context);
 533                } while (likely(stats_read_pending == 0));
 534
 535                rte_spinlock_unlock(&qconf->lock);
 536                rte_pause();
 537        }
 538}
 539
 540static int
 541l2fwd_launch_one_lcore(__rte_unused void *dummy)
 542{
 543        l2fwd_main_loop();
 544        return 0;
 545}
 546
 547/* display usage */
 548static void
 549l2fwd_usage(const char *prgname)
 550{
 551        printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
 552               "  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
 553               "  -q NQ: number of queue (=ports) per lcore (default is 1)\n"
 554                   "  -T PERIOD: statistics will be refreshed each PERIOD seconds (0 to disable, 10 default, 86400 maximum)\n"
 555                   "  -l set system default locale instead of default (\"C\" locale) for thousands separator in stats.",
 556               prgname);
 557}
 558
 559static int
 560l2fwd_parse_portmask(const char *portmask)
 561{
 562        char *end = NULL;
 563        unsigned long pm;
 564
 565        /* parse hexadecimal string */
 566        pm = strtoul(portmask, &end, 16);
 567        if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
 568                return 0;
 569
 570        return pm;
 571}
 572
 573static unsigned int
 574l2fwd_parse_nqueue(const char *q_arg)
 575{
 576        char *end = NULL;
 577        unsigned long n;
 578
 579        /* parse hexadecimal string */
 580        n = strtoul(q_arg, &end, 10);
 581        if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
 582                return 0;
 583        if (n == 0)
 584                return 0;
 585        if (n >= MAX_RX_QUEUE_PER_LCORE)
 586                return 0;
 587
 588        return n;
 589}
 590
 591static int
 592l2fwd_parse_timer_period(const char *q_arg)
 593{
 594        char *end = NULL;
 595        int n;
 596
 597        /* parse number string */
 598        n = strtol(q_arg, &end, 10);
 599        if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
 600                return -1;
 601        if (n >= MAX_TIMER_PERIOD)
 602                return -1;
 603
 604        return n;
 605}
 606
 607/* Parse the argument given in the command line of the application */
 608static int
 609l2fwd_parse_args(int argc, char **argv)
 610{
 611        int opt, ret;
 612        char **argvopt;
 613        int option_index;
 614        char *prgname = argv[0];
 615        static struct option lgopts[] = {
 616                {NULL, 0, 0, 0}
 617        };
 618
 619        argvopt = argv;
 620
 621        while ((opt = getopt_long(argc, argvopt, "p:q:T:l",
 622                                  lgopts, &option_index)) != EOF) {
 623
 624                switch (opt) {
 625                /* portmask */
 626                case 'p':
 627                        l2fwd_enabled_port_mask = l2fwd_parse_portmask(optarg);
 628                        if (l2fwd_enabled_port_mask == 0) {
 629                                printf("invalid portmask\n");
 630                                l2fwd_usage(prgname);
 631                                return -1;
 632                        }
 633                        break;
 634
 635                /* nqueue */
 636                case 'q':
 637                        l2fwd_rx_queue_per_lcore = l2fwd_parse_nqueue(optarg);
 638                        if (l2fwd_rx_queue_per_lcore == 0) {
 639                                printf("invalid queue number\n");
 640                                l2fwd_usage(prgname);
 641                                return -1;
 642                        }
 643                        break;
 644
 645                /* timer period */
 646                case 'T':
 647                        timer_period = l2fwd_parse_timer_period(optarg);
 648                        if (timer_period < 0) {
 649                                printf("invalid timer period\n");
 650                                l2fwd_usage(prgname);
 651                                return -1;
 652                        }
 653                        break;
 654
 655                /* For thousands separator in printf. */
 656                case 'l':
 657                        setlocale(LC_ALL, "");
 658                        break;
 659
 660                /* long options */
 661                case 0:
 662                        l2fwd_usage(prgname);
 663                        return -1;
 664
 665                default:
 666                        l2fwd_usage(prgname);
 667                        return -1;
 668                }
 669        }
 670
 671        if (optind >= 0)
 672                argv[optind-1] = prgname;
 673
 674        ret = optind-1;
 675        optind = 1; /* reset getopt lib */
 676        return ret;
 677}
 678
 679/* Check the link status of all ports in up to 9s, and print them finally */
 680static void
 681check_all_ports_link_status(uint32_t port_mask)
 682{
 683#define CHECK_INTERVAL 100 /* 100ms */
 684#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
 685        uint16_t portid;
 686        uint8_t count, all_ports_up, print_flag = 0;
 687        struct rte_eth_link link;
 688        int ret;
 689        char link_status_text[RTE_ETH_LINK_MAX_STR_LEN];
 690
 691        printf("\nChecking link status");
 692        fflush(stdout);
 693        for (count = 0; count <= MAX_CHECK_TIME; count++) {
 694                all_ports_up = 1;
 695                RTE_ETH_FOREACH_DEV(portid) {
 696                        if ((port_mask & (1 << portid)) == 0)
 697                                continue;
 698                        memset(&link, 0, sizeof(link));
 699                        ret = rte_eth_link_get_nowait(portid, &link);
 700                        if (ret < 0) {
 701                                all_ports_up = 0;
 702                                if (print_flag == 1)
 703                                        printf("Port %u link get failed: %s\n",
 704                                                portid, rte_strerror(-ret));
 705                                continue;
 706                        }
 707                        /* print link status if flag set */
 708                        if (print_flag == 1) {
 709                                rte_eth_link_to_str(link_status_text,
 710                                        sizeof(link_status_text), &link);
 711                                printf("Port %d %s\n", portid,
 712                                       link_status_text);
 713                                continue;
 714                        }
 715                        /* clear all_ports_up flag if any link down */
 716                        if (link.link_status == ETH_LINK_DOWN) {
 717                                all_ports_up = 0;
 718                                break;
 719                        }
 720                }
 721                /* after finally printing all link status, get out */
 722                if (print_flag == 1)
 723                        break;
 724
 725                if (all_ports_up == 0) {
 726                        printf(".");
 727                        fflush(stdout);
 728                        rte_delay_ms(CHECK_INTERVAL);
 729                }
 730
 731                /* set the print_flag if all ports up or timeout */
 732                if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
 733                        print_flag = 1;
 734                        printf("done\n");
 735                }
 736        }
 737}
 738
 739int
 740main(int argc, char **argv)
 741{
 742        struct lcore_queue_conf *qconf;
 743        unsigned lcore_id, rx_lcore_id;
 744        unsigned nb_ports_in_mask = 0;
 745        int ret;
 746        char name[RTE_JOBSTATS_NAMESIZE];
 747        uint16_t nb_ports;
 748        uint16_t nb_ports_available = 0;
 749        uint16_t portid, last_port;
 750        uint8_t i;
 751
 752        /* init EAL */
 753        ret = rte_eal_init(argc, argv);
 754        if (ret < 0)
 755                rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
 756        argc -= ret;
 757        argv += ret;
 758
 759        /* parse application arguments (after the EAL ones) */
 760        ret = l2fwd_parse_args(argc, argv);
 761        if (ret < 0)
 762                rte_exit(EXIT_FAILURE, "Invalid L2FWD arguments\n");
 763
 764        rte_timer_subsystem_init();
 765
 766        /* fetch default timer frequency. */
 767        hz = rte_get_timer_hz();
 768
 769        /* create the mbuf pool */
 770        l2fwd_pktmbuf_pool =
 771                rte_pktmbuf_pool_create("mbuf_pool", NB_MBUF, 32,
 772                        0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
 773        if (l2fwd_pktmbuf_pool == NULL)
 774                rte_exit(EXIT_FAILURE, "Cannot init mbuf pool\n");
 775
 776        nb_ports = rte_eth_dev_count_avail();
 777        if (nb_ports == 0)
 778                rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
 779
 780        /* reset l2fwd_dst_ports */
 781        for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++)
 782                l2fwd_dst_ports[portid] = 0;
 783        last_port = 0;
 784
 785        /*
 786         * Each logical core is assigned a dedicated TX queue on each port.
 787         */
 788        RTE_ETH_FOREACH_DEV(portid) {
 789                /* skip ports that are not enabled */
 790                if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
 791                        continue;
 792
 793                if (nb_ports_in_mask % 2) {
 794                        l2fwd_dst_ports[portid] = last_port;
 795                        l2fwd_dst_ports[last_port] = portid;
 796                } else
 797                        last_port = portid;
 798
 799                nb_ports_in_mask++;
 800        }
 801        if (nb_ports_in_mask % 2) {
 802                printf("Notice: odd number of ports in portmask.\n");
 803                l2fwd_dst_ports[last_port] = last_port;
 804        }
 805
 806        rx_lcore_id = 0;
 807        qconf = NULL;
 808
 809        /* Initialize the port/queue configuration of each logical core */
 810        RTE_ETH_FOREACH_DEV(portid) {
 811                /* skip ports that are not enabled */
 812                if ((l2fwd_enabled_port_mask & (1 << portid)) == 0)
 813                        continue;
 814
 815                /* get the lcore_id for this port */
 816                while (rte_lcore_is_enabled(rx_lcore_id) == 0 ||
 817                       lcore_queue_conf[rx_lcore_id].n_rx_port ==
 818                       l2fwd_rx_queue_per_lcore) {
 819                        rx_lcore_id++;
 820                        if (rx_lcore_id >= RTE_MAX_LCORE)
 821                                rte_exit(EXIT_FAILURE, "Not enough cores\n");
 822                }
 823
 824                if (qconf != &lcore_queue_conf[rx_lcore_id])
 825                        /* Assigned a new logical core in the loop above. */
 826                        qconf = &lcore_queue_conf[rx_lcore_id];
 827
 828                qconf->rx_port_list[qconf->n_rx_port] = portid;
 829                qconf->n_rx_port++;
 830                printf("Lcore %u: RX port %u\n", rx_lcore_id, portid);
 831        }
 832
 833        /* Initialise each port */
 834        RTE_ETH_FOREACH_DEV(portid) {
 835                struct rte_eth_dev_info dev_info;
 836                struct rte_eth_rxconf rxq_conf;
 837                struct rte_eth_txconf txq_conf;
 838                struct rte_eth_conf local_port_conf = port_conf;
 839
 840                /* skip ports that are not enabled */
 841                if ((l2fwd_enabled_port_mask & (1 << portid)) == 0) {
 842                        printf("Skipping disabled port %u\n", portid);
 843                        continue;
 844                }
 845                nb_ports_available++;
 846
 847                /* init port */
 848                printf("Initializing port %u... ", portid);
 849                fflush(stdout);
 850
 851                ret = rte_eth_dev_info_get(portid, &dev_info);
 852                if (ret != 0)
 853                        rte_exit(EXIT_FAILURE,
 854                                "Error during getting device (port %u) info: %s\n",
 855                                portid, strerror(-ret));
 856
 857                if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
 858                        local_port_conf.txmode.offloads |=
 859                                DEV_TX_OFFLOAD_MBUF_FAST_FREE;
 860                ret = rte_eth_dev_configure(portid, 1, 1, &local_port_conf);
 861                if (ret < 0)
 862                        rte_exit(EXIT_FAILURE, "Cannot configure device: err=%d, port=%u\n",
 863                                  ret, portid);
 864
 865                ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd,
 866                                                       &nb_txd);
 867                if (ret < 0)
 868                        rte_exit(EXIT_FAILURE,
 869                                 "Cannot adjust number of descriptors: err=%d, port=%u\n",
 870                                 ret, portid);
 871
 872                ret = rte_eth_macaddr_get(portid,
 873                                          &l2fwd_ports_eth_addr[portid]);
 874                if (ret < 0)
 875                        rte_exit(EXIT_FAILURE,
 876                                 "Cannot get MAC address: err=%d, port=%u\n",
 877                                 ret, portid);
 878
 879                /* init one RX queue */
 880                fflush(stdout);
 881                rxq_conf = dev_info.default_rxconf;
 882                rxq_conf.offloads = local_port_conf.rxmode.offloads;
 883                ret = rte_eth_rx_queue_setup(portid, 0, nb_rxd,
 884                                             rte_eth_dev_socket_id(portid),
 885                                             &rxq_conf,
 886                                             l2fwd_pktmbuf_pool);
 887                if (ret < 0)
 888                        rte_exit(EXIT_FAILURE, "rte_eth_rx_queue_setup:err=%d, port=%u\n",
 889                                  ret, portid);
 890
 891                /* init one TX queue on each port */
 892                txq_conf = dev_info.default_txconf;
 893                txq_conf.offloads = local_port_conf.txmode.offloads;
 894                fflush(stdout);
 895                ret = rte_eth_tx_queue_setup(portid, 0, nb_txd,
 896                                rte_eth_dev_socket_id(portid),
 897                                &txq_conf);
 898                if (ret < 0)
 899                        rte_exit(EXIT_FAILURE,
 900                        "rte_eth_tx_queue_setup:err=%d, port=%u\n",
 901                                ret, portid);
 902
 903                /* Initialize TX buffers */
 904                tx_buffer[portid] = rte_zmalloc_socket("tx_buffer",
 905                                RTE_ETH_TX_BUFFER_SIZE(MAX_PKT_BURST), 0,
 906                                rte_eth_dev_socket_id(portid));
 907                if (tx_buffer[portid] == NULL)
 908                        rte_exit(EXIT_FAILURE, "Cannot allocate buffer for tx on port %u\n",
 909                                        portid);
 910
 911                rte_eth_tx_buffer_init(tx_buffer[portid], MAX_PKT_BURST);
 912
 913                ret = rte_eth_tx_buffer_set_err_callback(tx_buffer[portid],
 914                                rte_eth_tx_buffer_count_callback,
 915                                &port_statistics[portid].dropped);
 916                if (ret < 0)
 917                        rte_exit(EXIT_FAILURE,
 918                        "Cannot set error callback for tx buffer on port %u\n",
 919                                 portid);
 920
 921                /* Start device */
 922                ret = rte_eth_dev_start(portid);
 923                if (ret < 0)
 924                        rte_exit(EXIT_FAILURE, "rte_eth_dev_start:err=%d, port=%u\n",
 925                                  ret, portid);
 926
 927                printf("done:\n");
 928
 929                ret = rte_eth_promiscuous_enable(portid);
 930                if (ret != 0) {
 931                        rte_exit(EXIT_FAILURE,
 932                                 "rte_eth_promiscuous_enable:err=%s, port=%u\n",
 933                                 rte_strerror(-ret), portid);
 934                        return ret;
 935
 936                }
 937
 938                printf("Port %u, MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n\n",
 939                                portid,
 940                                l2fwd_ports_eth_addr[portid].addr_bytes[0],
 941                                l2fwd_ports_eth_addr[portid].addr_bytes[1],
 942                                l2fwd_ports_eth_addr[portid].addr_bytes[2],
 943                                l2fwd_ports_eth_addr[portid].addr_bytes[3],
 944                                l2fwd_ports_eth_addr[portid].addr_bytes[4],
 945                                l2fwd_ports_eth_addr[portid].addr_bytes[5]);
 946
 947                /* initialize port stats */
 948                memset(&port_statistics, 0, sizeof(port_statistics));
 949        }
 950
 951        if (!nb_ports_available) {
 952                rte_exit(EXIT_FAILURE,
 953                        "All available ports are disabled. Please set portmask.\n");
 954        }
 955
 956        check_all_ports_link_status(l2fwd_enabled_port_mask);
 957
 958        drain_tsc = (hz + US_PER_S - 1) / US_PER_S * BURST_TX_DRAIN_US;
 959
 960        RTE_LCORE_FOREACH(lcore_id) {
 961                qconf = &lcore_queue_conf[lcore_id];
 962
 963                rte_spinlock_init(&qconf->lock);
 964
 965                if (rte_jobstats_context_init(&qconf->jobs_context) != 0)
 966                        rte_panic("Jobs stats context for core %u init failed\n", lcore_id);
 967
 968                if (qconf->n_rx_port == 0) {
 969                        RTE_LOG(INFO, L2FWD,
 970                                "lcore %u: no ports so no jobs stats context initialization\n",
 971                                lcore_id);
 972                        continue;
 973                }
 974                /* Add flush job.
 975                 * Set fixed period by setting min = max = initial period. Set target to
 976                 * zero as it is irrelevant for this job. */
 977                rte_jobstats_init(&qconf->flush_job, "flush", drain_tsc, drain_tsc,
 978                                drain_tsc, 0);
 979
 980                rte_timer_init(&qconf->flush_timer);
 981                ret = rte_timer_reset(&qconf->flush_timer, drain_tsc, PERIODICAL,
 982                                lcore_id, &l2fwd_flush_job, NULL);
 983
 984                if (ret < 0) {
 985                        rte_exit(1, "Failed to reset flush job timer for lcore %u: %s",
 986                                        lcore_id, rte_strerror(-ret));
 987                }
 988
 989                for (i = 0; i < qconf->n_rx_port; i++) {
 990                        struct rte_jobstats *job = &qconf->port_fwd_jobs[i];
 991
 992                        portid = qconf->rx_port_list[i];
 993                        printf("Setting forward job for port %u\n", portid);
 994
 995                        snprintf(name, RTE_DIM(name), "port %u fwd", portid);
 996                        /* Setup forward job.
 997                         * Set min, max and initial period. Set target to MAX_PKT_BURST as
 998                         * this is desired optimal RX/TX burst size. */
 999                        rte_jobstats_init(job, name, 0, drain_tsc, 0, MAX_PKT_BURST);
1000                        rte_jobstats_set_update_period_function(job, l2fwd_job_update_cb);
1001
1002                        rte_timer_init(&qconf->rx_timers[i]);
1003                        ret = rte_timer_reset(&qconf->rx_timers[i], 0, PERIODICAL, lcore_id,
1004                                        &l2fwd_fwd_job, (void *)(uintptr_t)i);
1005
1006                        if (ret < 0) {
1007                                rte_exit(1, "Failed to reset lcore %u port %u job timer: %s",
1008                                                lcore_id, qconf->rx_port_list[i], rte_strerror(-ret));
1009                        }
1010                }
1011        }
1012
1013        if (timer_period)
1014                rte_eal_alarm_set(timer_period * MS_PER_S, show_stats_cb, NULL);
1015        else
1016                RTE_LOG(INFO, L2FWD, "Stats display disabled\n");
1017
1018        /* launch per-lcore init on every lcore */
1019        rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, NULL, CALL_MAIN);
1020        RTE_LCORE_FOREACH_WORKER(lcore_id) {
1021                if (rte_eal_wait_lcore(lcore_id) < 0)
1022                        return -1;
1023        }
1024
1025        /* clean up the EAL */
1026        rte_eal_cleanup();
1027
1028        return 0;
1029}
1030