dpdk/examples/multi_process/symmetric_mp/main.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: BSD-3-Clause
   2 * Copyright(c) 2010-2014 Intel Corporation
   3 */
   4
   5/*
   6 * Sample application demonstrating how to do packet I/O in a multi-process
   7 * environment. The same code can be run as a primary process and as a
   8 * secondary process, just with a different proc-id parameter in each case
   9 * (apart from the EAL flag to indicate a secondary process).
  10 *
  11 * Each process will read from the same ports, given by the port-mask
  12 * parameter, which should be the same in each case, just using a different
  13 * queue per port as determined by the proc-id parameter.
  14 */
  15
  16#include <stdio.h>
  17#include <string.h>
  18#include <stdint.h>
  19#include <stdlib.h>
  20#include <stdarg.h>
  21#include <errno.h>
  22#include <sys/queue.h>
  23#include <getopt.h>
  24#include <signal.h>
  25#include <inttypes.h>
  26
  27#include <rte_common.h>
  28#include <rte_log.h>
  29#include <rte_memory.h>
  30#include <rte_launch.h>
  31#include <rte_eal.h>
  32#include <rte_per_lcore.h>
  33#include <rte_lcore.h>
  34#include <rte_branch_prediction.h>
  35#include <rte_debug.h>
  36#include <rte_interrupts.h>
  37#include <rte_ether.h>
  38#include <rte_ethdev.h>
  39#include <rte_mempool.h>
  40#include <rte_memcpy.h>
  41#include <rte_mbuf.h>
  42#include <rte_string_fns.h>
  43#include <rte_cycles.h>
  44
  45#define RTE_LOGTYPE_APP RTE_LOGTYPE_USER1
  46
  47#define NB_MBUFS 64*1024 /* use 64k mbufs */
  48#define MBUF_CACHE_SIZE 256
  49#define PKT_BURST 32
  50#define RX_RING_SIZE 1024
  51#define TX_RING_SIZE 1024
  52
  53#define PARAM_PROC_ID "proc-id"
  54#define PARAM_NUM_PROCS "num-procs"
  55
  56/* for each lcore, record the elements of the ports array to use */
  57struct lcore_ports{
  58        unsigned start_port;
  59        unsigned num_ports;
  60};
  61
  62/* structure to record the rx and tx packets. Put two per cache line as ports
  63 * used in pairs */
  64struct port_stats{
  65        unsigned rx;
  66        unsigned tx;
  67        unsigned drop;
  68} __rte_aligned(RTE_CACHE_LINE_SIZE / 2);
  69
  70static int proc_id = -1;
  71static unsigned num_procs = 0;
  72
  73static uint16_t ports[RTE_MAX_ETHPORTS];
  74static unsigned num_ports = 0;
  75
  76static struct lcore_ports lcore_ports[RTE_MAX_LCORE];
  77static struct port_stats pstats[RTE_MAX_ETHPORTS];
  78
  79/* prints the usage statement and quits with an error message */
  80static void
  81smp_usage(const char *prgname, const char *errmsg)
  82{
  83        printf("\nError: %s\n",errmsg);
  84        printf("\n%s [EAL options] -- -p <port mask> "
  85                        "--"PARAM_NUM_PROCS" <n>"
  86                        " --"PARAM_PROC_ID" <id>\n"
  87                        "-p         : a hex bitmask indicating what ports are to be used\n"
  88                        "--num-procs: the number of processes which will be used\n"
  89                        "--proc-id  : the id of the current process (id < num-procs)\n"
  90                        "\n",
  91                        prgname);
  92        exit(1);
  93}
  94
  95
  96/* signal handler configured for SIGTERM and SIGINT to print stats on exit */
  97static void
  98print_stats(int signum)
  99{
 100        unsigned i;
 101        printf("\nExiting on signal %d\n\n", signum);
 102        for (i = 0; i < num_ports; i++){
 103                const uint8_t p_num = ports[i];
 104                printf("Port %u: RX - %u, TX - %u, Drop - %u\n", (unsigned)p_num,
 105                                pstats[p_num].rx, pstats[p_num].tx, pstats[p_num].drop);
 106        }
 107        exit(0);
 108}
 109
 110/* Parse the argument given in the command line of the application */
 111static int
 112smp_parse_args(int argc, char **argv)
 113{
 114        int opt, ret;
 115        char **argvopt;
 116        int option_index;
 117        uint16_t i, port_mask = 0;
 118        char *prgname = argv[0];
 119        static struct option lgopts[] = {
 120                        {PARAM_NUM_PROCS, 1, 0, 0},
 121                        {PARAM_PROC_ID, 1, 0, 0},
 122                        {NULL, 0, 0, 0}
 123        };
 124
 125        argvopt = argv;
 126
 127        while ((opt = getopt_long(argc, argvopt, "p:", \
 128                        lgopts, &option_index)) != EOF) {
 129
 130                switch (opt) {
 131                case 'p':
 132                        port_mask = strtoull(optarg, NULL, 16);
 133                        break;
 134                        /* long options */
 135                case 0:
 136                        if (strncmp(lgopts[option_index].name, PARAM_NUM_PROCS, 8) == 0)
 137                                num_procs = atoi(optarg);
 138                        else if (strncmp(lgopts[option_index].name, PARAM_PROC_ID, 7) == 0)
 139                                proc_id = atoi(optarg);
 140                        break;
 141
 142                default:
 143                        smp_usage(prgname, "Cannot parse all command-line arguments\n");
 144                }
 145        }
 146
 147        if (optind >= 0)
 148                argv[optind-1] = prgname;
 149
 150        if (proc_id < 0)
 151                smp_usage(prgname, "Invalid or missing proc-id parameter\n");
 152        if (rte_eal_process_type() == RTE_PROC_PRIMARY && num_procs == 0)
 153                smp_usage(prgname, "Invalid or missing num-procs parameter\n");
 154        if (port_mask == 0)
 155                smp_usage(prgname, "Invalid or missing port mask\n");
 156
 157        /* get the port numbers from the port mask */
 158        RTE_ETH_FOREACH_DEV(i)
 159                if(port_mask & (1 << i))
 160                        ports[num_ports++] = (uint8_t)i;
 161
 162        ret = optind-1;
 163        optind = 1; /* reset getopt lib */
 164
 165        return ret;
 166}
 167
 168/*
 169 * Initialises a given port using global settings and with the rx buffers
 170 * coming from the mbuf_pool passed as parameter
 171 */
 172static inline int
 173smp_port_init(uint16_t port, struct rte_mempool *mbuf_pool,
 174               uint16_t num_queues)
 175{
 176        struct rte_eth_conf port_conf = {
 177                        .rxmode = {
 178                                .mq_mode        = RTE_ETH_MQ_RX_RSS,
 179                                .split_hdr_size = 0,
 180                                .offloads = RTE_ETH_RX_OFFLOAD_CHECKSUM,
 181                        },
 182                        .rx_adv_conf = {
 183                                .rss_conf = {
 184                                        .rss_key = NULL,
 185                                        .rss_hf = RTE_ETH_RSS_IP,
 186                                },
 187                        },
 188                        .txmode = {
 189                                .mq_mode = RTE_ETH_MQ_TX_NONE,
 190                        }
 191        };
 192        const uint16_t rx_rings = num_queues, tx_rings = num_queues;
 193        struct rte_eth_dev_info info;
 194        struct rte_eth_rxconf rxq_conf;
 195        struct rte_eth_txconf txq_conf;
 196        int retval;
 197        uint16_t q;
 198        uint16_t nb_rxd = RX_RING_SIZE;
 199        uint16_t nb_txd = TX_RING_SIZE;
 200        uint64_t rss_hf_tmp;
 201
 202        if (rte_eal_process_type() == RTE_PROC_SECONDARY)
 203                return 0;
 204
 205        if (!rte_eth_dev_is_valid_port(port))
 206                return -1;
 207
 208        printf("# Initialising port %u... ", port);
 209        fflush(stdout);
 210
 211        retval = rte_eth_dev_info_get(port, &info);
 212        if (retval != 0) {
 213                printf("Error during getting device (port %u) info: %s\n",
 214                                port, strerror(-retval));
 215                return retval;
 216        }
 217
 218        info.default_rxconf.rx_drop_en = 1;
 219
 220        if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE)
 221                port_conf.txmode.offloads |=
 222                        RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE;
 223
 224        rss_hf_tmp = port_conf.rx_adv_conf.rss_conf.rss_hf;
 225        port_conf.rx_adv_conf.rss_conf.rss_hf &= info.flow_type_rss_offloads;
 226        if (port_conf.rx_adv_conf.rss_conf.rss_hf != rss_hf_tmp) {
 227                printf("Port %u modified RSS hash function based on hardware support,"
 228                        "requested:%#"PRIx64" configured:%#"PRIx64"\n",
 229                        port,
 230                        rss_hf_tmp,
 231                        port_conf.rx_adv_conf.rss_conf.rss_hf);
 232        }
 233
 234        retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
 235        if (retval == -EINVAL) {
 236                printf("Port %u configuration failed. Re-attempting with HW checksum disabled.\n",
 237                        port);
 238                port_conf.rxmode.offloads &= ~(RTE_ETH_RX_OFFLOAD_CHECKSUM);
 239                retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
 240        }
 241
 242        if (retval == -ENOTSUP) {
 243                printf("Port %u configuration failed. Re-attempting with HW RSS disabled.\n",
 244                        port);
 245                port_conf.rxmode.mq_mode &= ~(RTE_ETH_MQ_RX_RSS);
 246                retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
 247        }
 248
 249        if (retval < 0)
 250                return retval;
 251
 252        retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
 253        if (retval < 0)
 254                return retval;
 255
 256        rxq_conf = info.default_rxconf;
 257        rxq_conf.offloads = port_conf.rxmode.offloads;
 258        for (q = 0; q < rx_rings; q ++) {
 259                retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
 260                                rte_eth_dev_socket_id(port),
 261                                &rxq_conf,
 262                                mbuf_pool);
 263                if (retval < 0)
 264                        return retval;
 265        }
 266
 267        txq_conf = info.default_txconf;
 268        txq_conf.offloads = port_conf.txmode.offloads;
 269        for (q = 0; q < tx_rings; q ++) {
 270                retval = rte_eth_tx_queue_setup(port, q, nb_txd,
 271                                rte_eth_dev_socket_id(port),
 272                                &txq_conf);
 273                if (retval < 0)
 274                        return retval;
 275        }
 276
 277        retval = rte_eth_promiscuous_enable(port);
 278        if (retval != 0)
 279                return retval;
 280
 281        retval  = rte_eth_dev_start(port);
 282        if (retval < 0)
 283                return retval;
 284
 285        return 0;
 286}
 287
 288/* Goes through each of the lcores and calculates what ports should
 289 * be used by that core. Fills in the global lcore_ports[] array.
 290 */
 291static void
 292assign_ports_to_cores(void)
 293{
 294
 295        const unsigned int lcores = rte_lcore_count();
 296        const unsigned port_pairs = num_ports / 2;
 297        const unsigned pairs_per_lcore = port_pairs / lcores;
 298        unsigned extra_pairs = port_pairs % lcores;
 299        unsigned ports_assigned = 0;
 300        unsigned i;
 301
 302        RTE_LCORE_FOREACH(i) {
 303                lcore_ports[i].start_port = ports_assigned;
 304                lcore_ports[i].num_ports = pairs_per_lcore * 2;
 305                if (extra_pairs > 0) {
 306                        lcore_ports[i].num_ports += 2;
 307                        extra_pairs--;
 308                }
 309                ports_assigned += lcore_ports[i].num_ports;
 310        }
 311}
 312
 313/* Main function used by the processing threads.
 314 * Prints out some configuration details for the thread and then begins
 315 * performing packet RX and TX.
 316 */
 317static int
 318lcore_main(void *arg __rte_unused)
 319{
 320        const unsigned id = rte_lcore_id();
 321        const unsigned start_port = lcore_ports[id].start_port;
 322        const unsigned end_port = start_port + lcore_ports[id].num_ports;
 323        const uint16_t q_id = (uint16_t)proc_id;
 324        unsigned p, i;
 325        char msgbuf[256];
 326        int msgbufpos = 0;
 327
 328        if (start_port == end_port){
 329                printf("Lcore %u has nothing to do\n", id);
 330                return 0;
 331        }
 332
 333        /* build up message in msgbuf before printing to decrease likelihood
 334         * of multi-core message interleaving.
 335         */
 336        msgbufpos += snprintf(msgbuf, sizeof(msgbuf) - msgbufpos,
 337                        "Lcore %u using ports ", id);
 338        for (p = start_port; p < end_port; p++){
 339                msgbufpos += snprintf(msgbuf + msgbufpos, sizeof(msgbuf) - msgbufpos,
 340                                "%u ", (unsigned)ports[p]);
 341        }
 342        printf("%s\n", msgbuf);
 343        printf("lcore %u using queue %u of each port\n", id, (unsigned)q_id);
 344
 345        /* handle packet I/O from the ports, reading and writing to the
 346         * queue number corresponding to our process number (not lcore id)
 347         */
 348
 349        for (;;) {
 350                struct rte_mbuf *buf[PKT_BURST];
 351
 352                for (p = start_port; p < end_port; p++) {
 353                        const uint8_t src = ports[p];
 354                        const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
 355                        const uint16_t rx_c = rte_eth_rx_burst(src, q_id, buf, PKT_BURST);
 356                        if (rx_c == 0)
 357                                continue;
 358                        pstats[src].rx += rx_c;
 359
 360                        const uint16_t tx_c = rte_eth_tx_burst(dst, q_id, buf, rx_c);
 361                        pstats[dst].tx += tx_c;
 362                        if (tx_c != rx_c) {
 363                                pstats[dst].drop += (rx_c - tx_c);
 364                                for (i = tx_c; i < rx_c; i++)
 365                                        rte_pktmbuf_free(buf[i]);
 366                        }
 367                }
 368        }
 369}
 370
 371/* Check the link status of all ports in up to 9s, and print them finally */
 372static void
 373check_all_ports_link_status(uint16_t port_num, uint32_t port_mask)
 374{
 375#define CHECK_INTERVAL 100 /* 100ms */
 376#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
 377        uint16_t portid;
 378        uint8_t count, all_ports_up, print_flag = 0;
 379        struct rte_eth_link link;
 380        int ret;
 381        char link_status_text[RTE_ETH_LINK_MAX_STR_LEN];
 382
 383        printf("\nChecking link status");
 384        fflush(stdout);
 385        for (count = 0; count <= MAX_CHECK_TIME; count++) {
 386                all_ports_up = 1;
 387                for (portid = 0; portid < port_num; portid++) {
 388                        if ((port_mask & (1 << portid)) == 0)
 389                                continue;
 390                        memset(&link, 0, sizeof(link));
 391                        ret = rte_eth_link_get_nowait(portid, &link);
 392                        if (ret < 0) {
 393                                all_ports_up = 0;
 394                                if (print_flag == 1)
 395                                        printf("Port %u link get failed: %s\n",
 396                                                portid, rte_strerror(-ret));
 397                                continue;
 398                        }
 399                        /* print link status if flag set */
 400                        if (print_flag == 1) {
 401                                rte_eth_link_to_str(link_status_text,
 402                                        sizeof(link_status_text), &link);
 403                                printf("Port %d %s\n", portid,
 404                                       link_status_text);
 405                                continue;
 406                        }
 407                        /* clear all_ports_up flag if any link down */
 408                        if (link.link_status == RTE_ETH_LINK_DOWN) {
 409                                all_ports_up = 0;
 410                                break;
 411                        }
 412                }
 413                /* after finally printing all link status, get out */
 414                if (print_flag == 1)
 415                        break;
 416
 417                if (all_ports_up == 0) {
 418                        printf(".");
 419                        fflush(stdout);
 420                        rte_delay_ms(CHECK_INTERVAL);
 421                }
 422
 423                /* set the print_flag if all ports up or timeout */
 424                if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
 425                        print_flag = 1;
 426                        printf("done\n");
 427                }
 428        }
 429}
 430
 431/* Main function.
 432 * Performs initialisation and then calls the lcore_main on each core
 433 * to do the packet-processing work.
 434 */
 435int
 436main(int argc, char **argv)
 437{
 438        static const char *_SMP_MBUF_POOL = "SMP_MBUF_POOL";
 439        int ret;
 440        unsigned i;
 441        enum rte_proc_type_t proc_type;
 442        struct rte_mempool *mp;
 443
 444        /* set up signal handlers to print stats on exit */
 445        signal(SIGINT, print_stats);
 446        signal(SIGTERM, print_stats);
 447
 448        /* initialise the EAL for all */
 449        ret = rte_eal_init(argc, argv);
 450        if (ret < 0)
 451                rte_exit(EXIT_FAILURE, "Cannot init EAL\n");
 452        argc -= ret;
 453        argv += ret;
 454
 455        /* determine the NIC devices available */
 456        if (rte_eth_dev_count_avail() == 0)
 457                rte_exit(EXIT_FAILURE, "No Ethernet ports - bye\n");
 458
 459        /* parse application arguments (those after the EAL ones) */
 460        smp_parse_args(argc, argv);
 461
 462        proc_type = rte_eal_process_type();
 463        mp = (proc_type == RTE_PROC_SECONDARY) ?
 464                        rte_mempool_lookup(_SMP_MBUF_POOL) :
 465                        rte_pktmbuf_pool_create(_SMP_MBUF_POOL, NB_MBUFS,
 466                                MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE,
 467                                rte_socket_id());
 468        if (mp == NULL)
 469                rte_exit(EXIT_FAILURE, "Cannot get memory pool for buffers\n");
 470
 471        /* Primary instance initialized. 8< */
 472        if (num_ports & 1)
 473                rte_exit(EXIT_FAILURE, "Application must use an even number of ports\n");
 474        for(i = 0; i < num_ports; i++){
 475                if(proc_type == RTE_PROC_PRIMARY)
 476                        if (smp_port_init(ports[i], mp, (uint16_t)num_procs) < 0)
 477                                rte_exit(EXIT_FAILURE, "Error initialising ports\n");
 478        }
 479        /* >8 End of primary instance initialization. */
 480
 481        if (proc_type == RTE_PROC_PRIMARY)
 482                check_all_ports_link_status((uint8_t)num_ports, (~0x0));
 483
 484        assign_ports_to_cores();
 485
 486        RTE_LOG(INFO, APP, "Finished Process Init.\n");
 487
 488        rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MAIN);
 489
 490        /* clean up the EAL */
 491        rte_eal_cleanup();
 492
 493        return 0;
 494}
 495