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