examples/load_balancer: remove example
This example can be removed because DPDK now has a range of libraries, especially rte_eventdev, that did not exist previously for load balancing, making this less relevant. Also, modern NIC cards have greater ability to do load balancing, e.g. using RSS, over a wider range of fields than earlier cards did. Signed-off-by: Ciara Power <ciara.power@intel.com> Acked-by: Stephen Hemminger <stephen@networkplumber.org> Acked-by: Hemant Agrawal <hemant.agrawal@nxp.com>
This commit is contained in:
parent
d82610b940
commit
ab6ebd7020
@ -1498,9 +1498,6 @@ F: doc/guides/sample_app_ug/l3_forward.rst
|
||||
F: examples/link_status_interrupt/
|
||||
F: doc/guides/sample_app_ug/link_status_intr.rst
|
||||
|
||||
F: examples/load_balancer/
|
||||
F: doc/guides/sample_app_ug/load_balancer.rst
|
||||
|
||||
L-threads - EXPERIMENTAL
|
||||
M: John McNamara <john.mcnamara@intel.com>
|
||||
F: examples/performance-thread/
|
||||
|
@ -262,6 +262,7 @@ Removed Items
|
||||
|
||||
* Exception Path
|
||||
* L3 Forwarding in a Virtualization Environment
|
||||
* Load Balancer
|
||||
* Netmap Compatibility
|
||||
* Quota and Watermark
|
||||
* vhost-scsi
|
||||
|
@ -30,7 +30,6 @@ Sample Applications User Guides
|
||||
l3_forward_power_man
|
||||
l3_forward_access_ctrl
|
||||
link_status_intr
|
||||
load_balancer
|
||||
server_node_efd
|
||||
service_cores
|
||||
multi_process
|
||||
|
@ -1,201 +0,0 @@
|
||||
.. SPDX-License-Identifier: BSD-3-Clause
|
||||
Copyright(c) 2010-2014 Intel Corporation.
|
||||
|
||||
Load Balancer Sample Application
|
||||
================================
|
||||
|
||||
The Load Balancer sample application demonstrates the concept of isolating the packet I/O task
|
||||
from the application-specific workload.
|
||||
Depending on the performance target,
|
||||
a number of logical cores (lcores) are dedicated to handle the interaction with the NIC ports (I/O lcores),
|
||||
while the rest of the lcores are dedicated to performing the application processing (worker lcores).
|
||||
The worker lcores are totally oblivious to the intricacies of the packet I/O activity and
|
||||
use the NIC-agnostic interface provided by software rings to exchange packets with the I/O cores.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
The architecture of the Load Balance application is presented in the following figure.
|
||||
|
||||
.. _figure_load_bal_app_arch:
|
||||
|
||||
.. figure:: img/load_bal_app_arch.*
|
||||
|
||||
Load Balancer Application Architecture
|
||||
|
||||
|
||||
For the sake of simplicity, the diagram illustrates a specific case of two I/O RX and two I/O TX lcores off loading the packet I/O
|
||||
overhead incurred by four NIC ports from four worker cores, with each I/O lcore handling RX/TX for two NIC ports.
|
||||
|
||||
I/O RX Logical Cores
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Each I/O RX lcore performs packet RX from its assigned NIC RX rings and then distributes the received packets to the worker threads.
|
||||
The application allows each I/O RX lcore to communicate with any of the worker threads,
|
||||
therefore each (I/O RX lcore, worker lcore) pair is connected through a dedicated single producer - single consumer software ring.
|
||||
|
||||
The worker lcore to handle the current packet is determined by reading a predefined 1-byte field from the input packet:
|
||||
|
||||
worker_id = packet[load_balancing_field] % n_workers
|
||||
|
||||
Since all the packets that are part of the same traffic flow are expected to have the same value for the load balancing field,
|
||||
this scheme also ensures that all the packets that are part of the same traffic flow are directed to the same worker lcore (flow affinity)
|
||||
in the same order they enter the system (packet ordering).
|
||||
|
||||
I/O TX Logical Cores
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Each I/O lcore owns the packet TX for a predefined set of NIC ports. To enable each worker thread to send packets to any NIC TX port,
|
||||
the application creates a software ring for each (worker lcore, NIC TX port) pair,
|
||||
with each I/O TX core handling those software rings that are associated with NIC ports that it handles.
|
||||
|
||||
Worker Logical Cores
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Each worker lcore reads packets from its set of input software rings and
|
||||
routes them to the NIC ports for transmission by dispatching them to output software rings.
|
||||
The routing logic is LPM based, with all the worker threads sharing the same LPM rules.
|
||||
|
||||
Compiling the Application
|
||||
-------------------------
|
||||
|
||||
To compile the sample application see :doc:`compiling`.
|
||||
|
||||
The application is located in the ``load_balancer`` sub-directory.
|
||||
|
||||
Running the Application
|
||||
-----------------------
|
||||
|
||||
To successfully run the application,
|
||||
the command line used to start the application has to be in sync with the traffic flows configured on the traffic generator side.
|
||||
|
||||
For examples of application command lines and traffic generator flows, please refer to the DPDK Test Report.
|
||||
For more details on how to set up and run the sample applications provided with DPDK package,
|
||||
please refer to the *DPDK Getting Started Guide*.
|
||||
|
||||
Explanation
|
||||
-----------
|
||||
|
||||
Application Configuration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The application run-time configuration is done through the application command line parameters.
|
||||
Any parameter that is not specified as mandatory is optional,
|
||||
with the default value hard-coded in the main.h header file from the application folder.
|
||||
|
||||
The list of application command line parameters is listed below:
|
||||
|
||||
#. --rx "(PORT, QUEUE, LCORE), ...": The list of NIC RX ports and queues handled by the I/O RX lcores.
|
||||
This parameter also implicitly defines the list of I/O RX lcores. This is a mandatory parameter.
|
||||
|
||||
#. --tx "(PORT, LCORE), ... ": The list of NIC TX ports handled by the I/O TX lcores.
|
||||
This parameter also implicitly defines the list of I/O TX lcores.
|
||||
This is a mandatory parameter.
|
||||
|
||||
#. --w "LCORE, ...": The list of the worker lcores. This is a mandatory parameter.
|
||||
|
||||
#. --lpm "IP / PREFIX => PORT; ...": The list of LPM rules used by the worker lcores for packet forwarding.
|
||||
This is a mandatory parameter.
|
||||
|
||||
#. --rsz "A, B, C, D": Ring sizes:
|
||||
|
||||
#. A = The size (in number of buffer descriptors) of each of the NIC RX rings read by the I/O RX lcores.
|
||||
|
||||
#. B = The size (in number of elements) of each of the software rings used by the I/O RX lcores to send packets to worker lcores.
|
||||
|
||||
#. C = The size (in number of elements) of each of the software rings used by the worker lcores to send packets to I/O TX lcores.
|
||||
|
||||
#. D = The size (in number of buffer descriptors) of each of the NIC TX rings written by I/O TX lcores.
|
||||
|
||||
#. --bsz "(A, B), (C, D), (E, F)": Burst sizes:
|
||||
|
||||
#. A = The I/O RX lcore read burst size from NIC RX.
|
||||
|
||||
#. B = The I/O RX lcore write burst size to the output software rings.
|
||||
|
||||
#. C = The worker lcore read burst size from the input software rings.
|
||||
|
||||
#. D = The worker lcore write burst size to the output software rings.
|
||||
|
||||
#. E = The I/O TX lcore read burst size from the input software rings.
|
||||
|
||||
#. F = The I/O TX lcore write burst size to the NIC TX.
|
||||
|
||||
#. --pos-lb POS: The position of the 1-byte field within the input packet used by the I/O RX lcores
|
||||
to identify the worker lcore for the current packet.
|
||||
This field needs to be within the first 64 bytes of the input packet.
|
||||
|
||||
The infrastructure of software rings connecting I/O lcores and worker lcores is built by the application
|
||||
as a result of the application configuration provided by the user through the application command line parameters.
|
||||
|
||||
A specific lcore performing the I/O RX role for a specific set of NIC ports can also perform the I/O TX role
|
||||
for the same or a different set of NIC ports.
|
||||
A specific lcore cannot perform both the I/O role (either RX or TX) and the worker role during the same session.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
./load_balancer -l 3-7 -n 4 -- --rx "(0,0,3),(1,0,3)" --tx "(0,3),(1,3)" --w "4,5,6,7" --lpm "1.0.0.0/24=>0; 1.0.1.0/24=>1;" --pos-lb 29
|
||||
|
||||
There is a single I/O lcore (lcore 3) that handles RX and TX for two NIC ports (ports 0 and 1) that
|
||||
handles packets to/from four worker lcores (lcores 4, 5, 6 and 7) that
|
||||
are assigned worker IDs 0 to 3 (worker ID for lcore 4 is 0, for lcore 5 is 1, for lcore 6 is 2 and for lcore 7 is 3).
|
||||
|
||||
Assuming that all the input packets are IPv4 packets with no VLAN label and the source IP address of the current packet is A.B.C.D,
|
||||
the worker lcore for the current packet is determined by byte D (which is byte 29).
|
||||
There are two LPM rules that are used by each worker lcore to route packets to the output NIC ports.
|
||||
|
||||
The following table illustrates the packet flow through the system for several possible traffic flows:
|
||||
|
||||
+------------+----------------+-----------------+------------------------------+--------------+
|
||||
| **Flow #** | **Source** | **Destination** | **Worker ID (Worker lcore)** | **Output** |
|
||||
| | **IP Address** | **IP Address** | | **NIC Port** |
|
||||
| | | | | |
|
||||
+============+================+=================+==============================+==============+
|
||||
| 1 | 0.0.0.0 | 1.0.0.1 | 0 (4) | 0 |
|
||||
| | | | | |
|
||||
+------------+----------------+-----------------+------------------------------+--------------+
|
||||
| 2 | 0.0.0.1 | 1.0.1.2 | 1 (5) | 1 |
|
||||
| | | | | |
|
||||
+------------+----------------+-----------------+------------------------------+--------------+
|
||||
| 3 | 0.0.0.14 | 1.0.0.3 | 2 (6) | 0 |
|
||||
| | | | | |
|
||||
+------------+----------------+-----------------+------------------------------+--------------+
|
||||
| 4 | 0.0.0.15 | 1.0.1.4 | 3 (7) | 1 |
|
||||
| | | | | |
|
||||
+------------+----------------+-----------------+------------------------------+--------------+
|
||||
|
||||
NUMA Support
|
||||
~~~~~~~~~~~~
|
||||
|
||||
The application has built-in performance enhancements for the NUMA case:
|
||||
|
||||
#. One buffer pool per each CPU socket.
|
||||
|
||||
#. One LPM table per each CPU socket.
|
||||
|
||||
#. Memory for the NIC RX or TX rings is allocated on the same socket with the lcore handling the respective ring.
|
||||
|
||||
In the case where multiple CPU sockets are used in the system,
|
||||
it is recommended to enable at least one lcore to fulfill the I/O role for the NIC ports that
|
||||
are directly attached to that CPU socket through the PCI Express* bus.
|
||||
It is always recommended to handle the packet I/O with lcores from the same CPU socket as the NICs.
|
||||
|
||||
Depending on whether the I/O RX lcore (same CPU socket as NIC RX),
|
||||
the worker lcore and the I/O TX lcore (same CPU socket as NIC TX) handling a specific input packet,
|
||||
are on the same or different CPU sockets, the following run-time scenarios are possible:
|
||||
|
||||
#. AAA: The packet is received, processed and transmitted without going across CPU sockets.
|
||||
|
||||
#. AAB: The packet is received and processed on socket A,
|
||||
but as it has to be transmitted on a NIC port connected to socket B,
|
||||
the packet is sent to socket B through software rings.
|
||||
|
||||
#. ABB: The packet is received on socket A, but as it has to be processed by a worker lcore on socket B,
|
||||
the packet is sent to socket B through software rings.
|
||||
The packet is transmitted by a NIC port connected to the same CPU socket as the worker lcore that processed it.
|
||||
|
||||
#. ABC: The packet is received on socket A, it is processed by an lcore on socket B,
|
||||
then it has to be transmitted out by a NIC connected to socket C.
|
||||
The performance price for crossing the CPU socket boundary is paid twice for this packet.
|
@ -48,7 +48,6 @@ ifeq ($(CONFIG_RTE_LIBRTE_LPM)$(CONFIG_RTE_LIBRTE_HASH),yy)
|
||||
DIRS-$(CONFIG_RTE_LIBRTE_POWER) += l3fwd-power
|
||||
endif
|
||||
DIRS-y += link_status_interrupt
|
||||
DIRS-$(CONFIG_RTE_LIBRTE_LPM) += load_balancer
|
||||
DIRS-y += multi_process
|
||||
DIRS-y += ntb
|
||||
DIRS-$(CONFIG_RTE_LIBRTE_REORDER) += packet_ordering
|
||||
|
@ -1,62 +0,0 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# Copyright(c) 2010-2014 Intel Corporation
|
||||
|
||||
# binary name
|
||||
APP = load_balancer
|
||||
|
||||
# all source are stored in SRCS-y
|
||||
SRCS-y := main.c config.c init.c runtime.c
|
||||
|
||||
# Build using pkg-config variables if possible
|
||||
ifeq ($(shell pkg-config --exists libdpdk && echo 0),0)
|
||||
|
||||
all: shared
|
||||
.PHONY: shared static
|
||||
shared: build/$(APP)-shared
|
||||
ln -sf $(APP)-shared build/$(APP)
|
||||
static: build/$(APP)-static
|
||||
ln -sf $(APP)-static build/$(APP)
|
||||
|
||||
PKGCONF=pkg-config --define-prefix
|
||||
|
||||
PC_FILE := $(shell $(PKGCONF) --path libdpdk)
|
||||
CFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk)
|
||||
LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk)
|
||||
LDFLAGS_STATIC = -Wl,-Bstatic $(shell $(PKGCONF) --static --libs libdpdk)
|
||||
|
||||
build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
|
||||
$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
|
||||
|
||||
build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
|
||||
$(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)
|
||||
|
||||
build:
|
||||
@mkdir -p $@
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
|
||||
test -d build && rmdir -p build || true
|
||||
|
||||
else # Build using legacy build system
|
||||
|
||||
ifeq ($(RTE_SDK),)
|
||||
$(error "Please define RTE_SDK environment variable")
|
||||
endif
|
||||
|
||||
# Default target, detect a build directory, by looking for a path with a .config
|
||||
RTE_TARGET ?= $(notdir $(abspath $(dir $(firstword $(wildcard $(RTE_SDK)/*/.config)))))
|
||||
|
||||
include $(RTE_SDK)/mk/rte.vars.mk
|
||||
|
||||
CFLAGS += -O3 -g
|
||||
CFLAGS += $(WERROR_FLAGS)
|
||||
|
||||
# workaround for a gcc bug with noreturn attribute
|
||||
# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
|
||||
ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
|
||||
CFLAGS_main.o += -Wno-return-type
|
||||
endif
|
||||
|
||||
include $(RTE_SDK)/mk/rte.extapp.mk
|
||||
endif
|
File diff suppressed because it is too large
Load Diff
@ -1,537 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2010-2014 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <rte_common.h>
|
||||
#include <rte_byteorder.h>
|
||||
#include <rte_log.h>
|
||||
#include <rte_memory.h>
|
||||
#include <rte_memcpy.h>
|
||||
#include <rte_eal.h>
|
||||
#include <rte_launch.h>
|
||||
#include <rte_atomic.h>
|
||||
#include <rte_cycles.h>
|
||||
#include <rte_prefetch.h>
|
||||
#include <rte_lcore.h>
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_branch_prediction.h>
|
||||
#include <rte_interrupts.h>
|
||||
#include <rte_random.h>
|
||||
#include <rte_debug.h>
|
||||
#include <rte_ether.h>
|
||||
#include <rte_ethdev.h>
|
||||
#include <rte_ring.h>
|
||||
#include <rte_mempool.h>
|
||||
#include <rte_mbuf.h>
|
||||
#include <rte_string_fns.h>
|
||||
#include <rte_ip.h>
|
||||
#include <rte_tcp.h>
|
||||
#include <rte_lpm.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
static struct rte_eth_conf port_conf = {
|
||||
.rxmode = {
|
||||
.mq_mode = ETH_MQ_RX_RSS,
|
||||
.split_hdr_size = 0,
|
||||
.offloads = DEV_RX_OFFLOAD_CHECKSUM,
|
||||
},
|
||||
.rx_adv_conf = {
|
||||
.rss_conf = {
|
||||
.rss_key = NULL,
|
||||
.rss_hf = ETH_RSS_IP,
|
||||
},
|
||||
},
|
||||
.txmode = {
|
||||
.mq_mode = ETH_MQ_TX_NONE,
|
||||
},
|
||||
};
|
||||
|
||||
static void
|
||||
app_assign_worker_ids(void)
|
||||
{
|
||||
uint32_t lcore, worker_id;
|
||||
|
||||
/* Assign ID for each worker */
|
||||
worker_id = 0;
|
||||
for (lcore = 0; lcore < APP_MAX_LCORES; lcore ++) {
|
||||
struct app_lcore_params_worker *lp_worker = &app.lcore_params[lcore].worker;
|
||||
|
||||
if (app.lcore_params[lcore].type != e_APP_LCORE_WORKER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lp_worker->worker_id = worker_id;
|
||||
worker_id ++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
app_init_mbuf_pools(void)
|
||||
{
|
||||
unsigned socket, lcore;
|
||||
|
||||
/* Init the buffer pools */
|
||||
for (socket = 0; socket < APP_MAX_SOCKETS; socket ++) {
|
||||
char name[32];
|
||||
if (app_is_socket_used(socket) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "mbuf_pool_%u", socket);
|
||||
printf("Creating the mbuf pool for socket %u ...\n", socket);
|
||||
app.pools[socket] = rte_pktmbuf_pool_create(
|
||||
name, APP_DEFAULT_MEMPOOL_BUFFERS,
|
||||
APP_DEFAULT_MEMPOOL_CACHE_SIZE,
|
||||
0, APP_DEFAULT_MBUF_DATA_SIZE, socket);
|
||||
if (app.pools[socket] == NULL) {
|
||||
rte_panic("Cannot create mbuf pool on socket %u\n", socket);
|
||||
}
|
||||
}
|
||||
|
||||
for (lcore = 0; lcore < APP_MAX_LCORES; lcore ++) {
|
||||
if (app.lcore_params[lcore].type == e_APP_LCORE_DISABLED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
socket = rte_lcore_to_socket_id(lcore);
|
||||
app.lcore_params[lcore].pool = app.pools[socket];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
app_init_lpm_tables(void)
|
||||
{
|
||||
unsigned socket, lcore;
|
||||
|
||||
/* Init the LPM tables */
|
||||
for (socket = 0; socket < APP_MAX_SOCKETS; socket ++) {
|
||||
char name[32];
|
||||
uint32_t rule;
|
||||
|
||||
if (app_is_socket_used(socket) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct rte_lpm_config lpm_config;
|
||||
|
||||
lpm_config.max_rules = APP_MAX_LPM_RULES;
|
||||
lpm_config.number_tbl8s = 256;
|
||||
lpm_config.flags = 0;
|
||||
snprintf(name, sizeof(name), "lpm_table_%u", socket);
|
||||
printf("Creating the LPM table for socket %u ...\n", socket);
|
||||
app.lpm_tables[socket] = rte_lpm_create(
|
||||
name,
|
||||
socket,
|
||||
&lpm_config);
|
||||
if (app.lpm_tables[socket] == NULL) {
|
||||
rte_panic("Unable to create LPM table on socket %u\n", socket);
|
||||
}
|
||||
|
||||
for (rule = 0; rule < app.n_lpm_rules; rule ++) {
|
||||
int ret;
|
||||
|
||||
ret = rte_lpm_add(app.lpm_tables[socket],
|
||||
app.lpm_rules[rule].ip,
|
||||
app.lpm_rules[rule].depth,
|
||||
app.lpm_rules[rule].if_out);
|
||||
|
||||
if (ret < 0) {
|
||||
rte_panic("Unable to add entry %u (%x/%u => %u) to the LPM table on socket %u (%d)\n",
|
||||
(unsigned) rule,
|
||||
(unsigned) app.lpm_rules[rule].ip,
|
||||
(unsigned) app.lpm_rules[rule].depth,
|
||||
(unsigned) app.lpm_rules[rule].if_out,
|
||||
socket,
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (lcore = 0; lcore < APP_MAX_LCORES; lcore ++) {
|
||||
if (app.lcore_params[lcore].type != e_APP_LCORE_WORKER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
socket = rte_lcore_to_socket_id(lcore);
|
||||
app.lcore_params[lcore].worker.lpm_table = app.lpm_tables[socket];
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
app_init_rings_rx(void)
|
||||
{
|
||||
unsigned lcore;
|
||||
|
||||
/* Initialize the rings for the RX side */
|
||||
for (lcore = 0; lcore < APP_MAX_LCORES; lcore ++) {
|
||||
struct app_lcore_params_io *lp_io = &app.lcore_params[lcore].io;
|
||||
unsigned socket_io, lcore_worker;
|
||||
|
||||
if ((app.lcore_params[lcore].type != e_APP_LCORE_IO) ||
|
||||
(lp_io->rx.n_nic_queues == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
socket_io = rte_lcore_to_socket_id(lcore);
|
||||
|
||||
for (lcore_worker = 0; lcore_worker < APP_MAX_LCORES; lcore_worker ++) {
|
||||
char name[32];
|
||||
struct app_lcore_params_worker *lp_worker = &app.lcore_params[lcore_worker].worker;
|
||||
struct rte_ring *ring = NULL;
|
||||
|
||||
if (app.lcore_params[lcore_worker].type != e_APP_LCORE_WORKER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Creating ring to connect I/O lcore %u (socket %u) with worker lcore %u ...\n",
|
||||
lcore,
|
||||
socket_io,
|
||||
lcore_worker);
|
||||
snprintf(name, sizeof(name), "app_ring_rx_s%u_io%u_w%u",
|
||||
socket_io,
|
||||
lcore,
|
||||
lcore_worker);
|
||||
ring = rte_ring_create(
|
||||
name,
|
||||
app.ring_rx_size,
|
||||
socket_io,
|
||||
RING_F_SP_ENQ | RING_F_SC_DEQ);
|
||||
if (ring == NULL) {
|
||||
rte_panic("Cannot create ring to connect I/O core %u with worker core %u\n",
|
||||
lcore,
|
||||
lcore_worker);
|
||||
}
|
||||
|
||||
lp_io->rx.rings[lp_io->rx.n_rings] = ring;
|
||||
lp_io->rx.n_rings ++;
|
||||
|
||||
lp_worker->rings_in[lp_worker->n_rings_in] = ring;
|
||||
lp_worker->n_rings_in ++;
|
||||
}
|
||||
}
|
||||
|
||||
for (lcore = 0; lcore < APP_MAX_LCORES; lcore ++) {
|
||||
struct app_lcore_params_io *lp_io = &app.lcore_params[lcore].io;
|
||||
|
||||
if ((app.lcore_params[lcore].type != e_APP_LCORE_IO) ||
|
||||
(lp_io->rx.n_nic_queues == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lp_io->rx.n_rings != app_get_lcores_worker()) {
|
||||
rte_panic("Algorithmic error (I/O RX rings)\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (lcore = 0; lcore < APP_MAX_LCORES; lcore ++) {
|
||||
struct app_lcore_params_worker *lp_worker = &app.lcore_params[lcore].worker;
|
||||
|
||||
if (app.lcore_params[lcore].type != e_APP_LCORE_WORKER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lp_worker->n_rings_in != app_get_lcores_io_rx()) {
|
||||
rte_panic("Algorithmic error (worker input rings)\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
app_init_rings_tx(void)
|
||||
{
|
||||
unsigned lcore;
|
||||
|
||||
/* Initialize the rings for the TX side */
|
||||
for (lcore = 0; lcore < APP_MAX_LCORES; lcore ++) {
|
||||
struct app_lcore_params_worker *lp_worker = &app.lcore_params[lcore].worker;
|
||||
unsigned port;
|
||||
|
||||
if (app.lcore_params[lcore].type != e_APP_LCORE_WORKER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (port = 0; port < APP_MAX_NIC_PORTS; port ++) {
|
||||
char name[32];
|
||||
struct app_lcore_params_io *lp_io = NULL;
|
||||
struct rte_ring *ring;
|
||||
uint32_t socket_io, lcore_io;
|
||||
|
||||
if (app.nic_tx_port_mask[port] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (app_get_lcore_for_nic_tx(port, &lcore_io) < 0) {
|
||||
rte_panic("Algorithmic error (no I/O core to handle TX of port %u)\n",
|
||||
port);
|
||||
}
|
||||
|
||||
lp_io = &app.lcore_params[lcore_io].io;
|
||||
socket_io = rte_lcore_to_socket_id(lcore_io);
|
||||
|
||||
printf("Creating ring to connect worker lcore %u with TX port %u (through I/O lcore %u) (socket %u) ...\n",
|
||||
lcore, port, (unsigned)lcore_io, (unsigned)socket_io);
|
||||
snprintf(name, sizeof(name), "app_ring_tx_s%u_w%u_p%u", socket_io, lcore, port);
|
||||
ring = rte_ring_create(
|
||||
name,
|
||||
app.ring_tx_size,
|
||||
socket_io,
|
||||
RING_F_SP_ENQ | RING_F_SC_DEQ);
|
||||
if (ring == NULL) {
|
||||
rte_panic("Cannot create ring to connect worker core %u with TX port %u\n",
|
||||
lcore,
|
||||
port);
|
||||
}
|
||||
|
||||
lp_worker->rings_out[port] = ring;
|
||||
lp_io->tx.rings[port][lp_worker->worker_id] = ring;
|
||||
}
|
||||
}
|
||||
|
||||
for (lcore = 0; lcore < APP_MAX_LCORES; lcore ++) {
|
||||
struct app_lcore_params_io *lp_io = &app.lcore_params[lcore].io;
|
||||
unsigned i;
|
||||
|
||||
if ((app.lcore_params[lcore].type != e_APP_LCORE_IO) ||
|
||||
(lp_io->tx.n_nic_ports == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < lp_io->tx.n_nic_ports; i ++){
|
||||
unsigned port, j;
|
||||
|
||||
port = lp_io->tx.nic_ports[i];
|
||||
for (j = 0; j < app_get_lcores_worker(); j ++) {
|
||||
if (lp_io->tx.rings[port][j] == NULL) {
|
||||
rte_panic("Algorithmic error (I/O TX rings)\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the link status of all ports in up to 9s, and print them finally */
|
||||
static void
|
||||
check_all_ports_link_status(uint16_t port_num, uint32_t port_mask)
|
||||
{
|
||||
#define CHECK_INTERVAL 100 /* 100ms */
|
||||
#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
|
||||
uint16_t portid;
|
||||
uint8_t count, all_ports_up, print_flag = 0;
|
||||
struct rte_eth_link link;
|
||||
int ret;
|
||||
uint32_t n_rx_queues, n_tx_queues;
|
||||
|
||||
printf("\nChecking link status");
|
||||
fflush(stdout);
|
||||
for (count = 0; count <= MAX_CHECK_TIME; count++) {
|
||||
all_ports_up = 1;
|
||||
for (portid = 0; portid < port_num; portid++) {
|
||||
if ((port_mask & (1 << portid)) == 0)
|
||||
continue;
|
||||
n_rx_queues = app_get_nic_rx_queues_per_port(portid);
|
||||
n_tx_queues = app.nic_tx_port_mask[portid];
|
||||
if ((n_rx_queues == 0) && (n_tx_queues == 0))
|
||||
continue;
|
||||
memset(&link, 0, sizeof(link));
|
||||
ret = rte_eth_link_get_nowait(portid, &link);
|
||||
if (ret < 0) {
|
||||
all_ports_up = 0;
|
||||
if (print_flag == 1)
|
||||
printf("Port %u link get failed: %s\n",
|
||||
portid, rte_strerror(-ret));
|
||||
continue;
|
||||
}
|
||||
/* print link status if flag set */
|
||||
if (print_flag == 1) {
|
||||
if (link.link_status)
|
||||
printf(
|
||||
"Port%d Link Up - speed %uMbps - %s\n",
|
||||
portid, link.link_speed,
|
||||
(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
|
||||
("full-duplex") : ("half-duplex\n"));
|
||||
else
|
||||
printf("Port %d Link Down\n", portid);
|
||||
continue;
|
||||
}
|
||||
/* clear all_ports_up flag if any link down */
|
||||
if (link.link_status == ETH_LINK_DOWN) {
|
||||
all_ports_up = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* after finally printing all link status, get out */
|
||||
if (print_flag == 1)
|
||||
break;
|
||||
|
||||
if (all_ports_up == 0) {
|
||||
printf(".");
|
||||
fflush(stdout);
|
||||
rte_delay_ms(CHECK_INTERVAL);
|
||||
}
|
||||
|
||||
/* set the print_flag if all ports up or timeout */
|
||||
if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
|
||||
print_flag = 1;
|
||||
printf("done\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
app_init_nics(void)
|
||||
{
|
||||
unsigned socket;
|
||||
uint32_t lcore;
|
||||
uint16_t port;
|
||||
uint8_t queue;
|
||||
int ret;
|
||||
uint32_t n_rx_queues, n_tx_queues;
|
||||
|
||||
/* Init NIC ports and queues, then start the ports */
|
||||
for (port = 0; port < APP_MAX_NIC_PORTS; port ++) {
|
||||
struct rte_mempool *pool;
|
||||
uint16_t nic_rx_ring_size;
|
||||
uint16_t nic_tx_ring_size;
|
||||
struct rte_eth_rxconf rxq_conf;
|
||||
struct rte_eth_txconf txq_conf;
|
||||
struct rte_eth_dev_info dev_info;
|
||||
struct rte_eth_conf local_port_conf = port_conf;
|
||||
|
||||
n_rx_queues = app_get_nic_rx_queues_per_port(port);
|
||||
n_tx_queues = app.nic_tx_port_mask[port];
|
||||
|
||||
if ((n_rx_queues == 0) && (n_tx_queues == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Init port */
|
||||
printf("Initializing NIC port %u ...\n", port);
|
||||
|
||||
ret = rte_eth_dev_info_get(port, &dev_info);
|
||||
if (ret != 0)
|
||||
rte_panic("Error during getting device (port %u) info: %s\n",
|
||||
port, strerror(-ret));
|
||||
|
||||
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
|
||||
local_port_conf.txmode.offloads |=
|
||||
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
|
||||
|
||||
local_port_conf.rx_adv_conf.rss_conf.rss_hf &=
|
||||
dev_info.flow_type_rss_offloads;
|
||||
if (local_port_conf.rx_adv_conf.rss_conf.rss_hf !=
|
||||
port_conf.rx_adv_conf.rss_conf.rss_hf) {
|
||||
printf("Port %u modified RSS hash function based on hardware support,"
|
||||
"requested:%#"PRIx64" configured:%#"PRIx64"\n",
|
||||
port,
|
||||
port_conf.rx_adv_conf.rss_conf.rss_hf,
|
||||
local_port_conf.rx_adv_conf.rss_conf.rss_hf);
|
||||
}
|
||||
|
||||
ret = rte_eth_dev_configure(
|
||||
port,
|
||||
(uint8_t) n_rx_queues,
|
||||
(uint8_t) n_tx_queues,
|
||||
&local_port_conf);
|
||||
if (ret < 0) {
|
||||
rte_panic("Cannot init NIC port %u (%d)\n", port, ret);
|
||||
}
|
||||
|
||||
ret = rte_eth_promiscuous_enable(port);
|
||||
if (ret != 0)
|
||||
rte_panic("Cannot enable promiscuous mode on port %u (%s)\n",
|
||||
port, rte_strerror(-ret));
|
||||
|
||||
nic_rx_ring_size = app.nic_rx_ring_size;
|
||||
nic_tx_ring_size = app.nic_tx_ring_size;
|
||||
ret = rte_eth_dev_adjust_nb_rx_tx_desc(
|
||||
port, &nic_rx_ring_size, &nic_tx_ring_size);
|
||||
if (ret < 0) {
|
||||
rte_panic("Cannot adjust number of descriptors for port %u (%d)\n",
|
||||
port, ret);
|
||||
}
|
||||
app.nic_rx_ring_size = nic_rx_ring_size;
|
||||
app.nic_tx_ring_size = nic_tx_ring_size;
|
||||
|
||||
rxq_conf = dev_info.default_rxconf;
|
||||
rxq_conf.offloads = local_port_conf.rxmode.offloads;
|
||||
/* Init RX queues */
|
||||
for (queue = 0; queue < APP_MAX_RX_QUEUES_PER_NIC_PORT; queue ++) {
|
||||
if (app.nic_rx_queue_mask[port][queue] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
app_get_lcore_for_nic_rx(port, queue, &lcore);
|
||||
socket = rte_lcore_to_socket_id(lcore);
|
||||
pool = app.lcore_params[lcore].pool;
|
||||
|
||||
printf("Initializing NIC port %u RX queue %u ...\n",
|
||||
port, queue);
|
||||
ret = rte_eth_rx_queue_setup(
|
||||
port,
|
||||
queue,
|
||||
(uint16_t) app.nic_rx_ring_size,
|
||||
socket,
|
||||
&rxq_conf,
|
||||
pool);
|
||||
if (ret < 0) {
|
||||
rte_panic("Cannot init RX queue %u for port %u (%d)\n",
|
||||
queue, port, ret);
|
||||
}
|
||||
}
|
||||
|
||||
txq_conf = dev_info.default_txconf;
|
||||
txq_conf.offloads = local_port_conf.txmode.offloads;
|
||||
/* Init TX queues */
|
||||
if (app.nic_tx_port_mask[port] == 1) {
|
||||
app_get_lcore_for_nic_tx(port, &lcore);
|
||||
socket = rte_lcore_to_socket_id(lcore);
|
||||
printf("Initializing NIC port %u TX queue 0 ...\n",
|
||||
port);
|
||||
ret = rte_eth_tx_queue_setup(
|
||||
port,
|
||||
0,
|
||||
(uint16_t) app.nic_tx_ring_size,
|
||||
socket,
|
||||
&txq_conf);
|
||||
if (ret < 0) {
|
||||
rte_panic("Cannot init TX queue 0 for port %d (%d)\n",
|
||||
port,
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
/* Start port */
|
||||
ret = rte_eth_dev_start(port);
|
||||
if (ret < 0) {
|
||||
rte_panic("Cannot start port %d (%d)\n", port, ret);
|
||||
}
|
||||
}
|
||||
|
||||
check_all_ports_link_status(APP_MAX_NIC_PORTS, (~0x0));
|
||||
}
|
||||
|
||||
void
|
||||
app_init(void)
|
||||
{
|
||||
app_assign_worker_ids();
|
||||
app_init_mbuf_pools();
|
||||
app_init_lpm_tables();
|
||||
app_init_rings_rx();
|
||||
app_init_rings_tx();
|
||||
app_init_nics();
|
||||
|
||||
printf("Initialization completed.\n");
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2010-2014 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <rte_common.h>
|
||||
#include <rte_byteorder.h>
|
||||
#include <rte_log.h>
|
||||
#include <rte_memory.h>
|
||||
#include <rte_memcpy.h>
|
||||
#include <rte_eal.h>
|
||||
#include <rte_launch.h>
|
||||
#include <rte_atomic.h>
|
||||
#include <rte_cycles.h>
|
||||
#include <rte_prefetch.h>
|
||||
#include <rte_lcore.h>
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_branch_prediction.h>
|
||||
#include <rte_interrupts.h>
|
||||
#include <rte_random.h>
|
||||
#include <rte_debug.h>
|
||||
#include <rte_ether.h>
|
||||
#include <rte_ethdev.h>
|
||||
#include <rte_mempool.h>
|
||||
#include <rte_mbuf.h>
|
||||
#include <rte_ip.h>
|
||||
#include <rte_tcp.h>
|
||||
#include <rte_lpm.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
uint32_t lcore;
|
||||
int ret;
|
||||
|
||||
/* Init EAL */
|
||||
ret = rte_eal_init(argc, argv);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
argc -= ret;
|
||||
argv += ret;
|
||||
|
||||
/* Parse application arguments (after the EAL ones) */
|
||||
ret = app_parse_args(argc, argv);
|
||||
if (ret < 0) {
|
||||
app_print_usage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Init */
|
||||
app_init();
|
||||
app_print_params();
|
||||
|
||||
/* Launch per-lcore init on every lcore */
|
||||
rte_eal_mp_remote_launch(app_lcore_main_loop, NULL, CALL_MASTER);
|
||||
RTE_LCORE_FOREACH_SLAVE(lcore) {
|
||||
if (rte_eal_wait_lcore(lcore) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,351 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2010-2014 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef _MAIN_H_
|
||||
#define _MAIN_H_
|
||||
|
||||
/* Logical cores */
|
||||
#ifndef APP_MAX_SOCKETS
|
||||
#define APP_MAX_SOCKETS 2
|
||||
#endif
|
||||
|
||||
#ifndef APP_MAX_LCORES
|
||||
#define APP_MAX_LCORES RTE_MAX_LCORE
|
||||
#endif
|
||||
|
||||
#ifndef APP_MAX_NIC_PORTS
|
||||
#define APP_MAX_NIC_PORTS RTE_MAX_ETHPORTS
|
||||
#endif
|
||||
|
||||
#ifndef APP_MAX_RX_QUEUES_PER_NIC_PORT
|
||||
#define APP_MAX_RX_QUEUES_PER_NIC_PORT 128
|
||||
#endif
|
||||
|
||||
#ifndef APP_MAX_TX_QUEUES_PER_NIC_PORT
|
||||
#define APP_MAX_TX_QUEUES_PER_NIC_PORT 128
|
||||
#endif
|
||||
|
||||
#ifndef APP_MAX_IO_LCORES
|
||||
#if (APP_MAX_LCORES > 16)
|
||||
#define APP_MAX_IO_LCORES 16
|
||||
#else
|
||||
#define APP_MAX_IO_LCORES APP_MAX_LCORES
|
||||
#endif
|
||||
#endif
|
||||
#if (APP_MAX_IO_LCORES > APP_MAX_LCORES)
|
||||
#error "APP_MAX_IO_LCORES is too big"
|
||||
#endif
|
||||
|
||||
#ifndef APP_MAX_NIC_RX_QUEUES_PER_IO_LCORE
|
||||
#define APP_MAX_NIC_RX_QUEUES_PER_IO_LCORE 16
|
||||
#endif
|
||||
|
||||
#ifndef APP_MAX_NIC_TX_PORTS_PER_IO_LCORE
|
||||
#define APP_MAX_NIC_TX_PORTS_PER_IO_LCORE 16
|
||||
#endif
|
||||
#if (APP_MAX_NIC_TX_PORTS_PER_IO_LCORE > APP_MAX_NIC_PORTS)
|
||||
#error "APP_MAX_NIC_TX_PORTS_PER_IO_LCORE too big"
|
||||
#endif
|
||||
|
||||
#ifndef APP_MAX_WORKER_LCORES
|
||||
#if (APP_MAX_LCORES > 16)
|
||||
#define APP_MAX_WORKER_LCORES 16
|
||||
#else
|
||||
#define APP_MAX_WORKER_LCORES APP_MAX_LCORES
|
||||
#endif
|
||||
#endif
|
||||
#if (APP_MAX_WORKER_LCORES > APP_MAX_LCORES)
|
||||
#error "APP_MAX_WORKER_LCORES is too big"
|
||||
#endif
|
||||
|
||||
|
||||
/* Mempools */
|
||||
#ifndef APP_DEFAULT_MBUF_DATA_SIZE
|
||||
#define APP_DEFAULT_MBUF_DATA_SIZE RTE_MBUF_DEFAULT_BUF_SIZE
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_MEMPOOL_BUFFERS
|
||||
#define APP_DEFAULT_MEMPOOL_BUFFERS 8192 * 4
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_MEMPOOL_CACHE_SIZE
|
||||
#define APP_DEFAULT_MEMPOOL_CACHE_SIZE 256
|
||||
#endif
|
||||
|
||||
/* LPM Tables */
|
||||
#ifndef APP_MAX_LPM_RULES
|
||||
#define APP_MAX_LPM_RULES 1024
|
||||
#endif
|
||||
|
||||
/* NIC RX */
|
||||
#ifndef APP_DEFAULT_NIC_RX_RING_SIZE
|
||||
#define APP_DEFAULT_NIC_RX_RING_SIZE 1024
|
||||
#endif
|
||||
|
||||
/*
|
||||
* RX and TX Prefetch, Host, and Write-back threshold values should be
|
||||
* carefully set for optimal performance. Consult the network
|
||||
* controller's datasheet and supporting DPDK documentation for guidance
|
||||
* on how these parameters should be set.
|
||||
*/
|
||||
#ifndef APP_DEFAULT_NIC_RX_PTHRESH
|
||||
#define APP_DEFAULT_NIC_RX_PTHRESH 8
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_NIC_RX_HTHRESH
|
||||
#define APP_DEFAULT_NIC_RX_HTHRESH 8
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_NIC_RX_WTHRESH
|
||||
#define APP_DEFAULT_NIC_RX_WTHRESH 4
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_NIC_RX_FREE_THRESH
|
||||
#define APP_DEFAULT_NIC_RX_FREE_THRESH 64
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_NIC_RX_DROP_EN
|
||||
#define APP_DEFAULT_NIC_RX_DROP_EN 0
|
||||
#endif
|
||||
|
||||
/* NIC TX */
|
||||
#ifndef APP_DEFAULT_NIC_TX_RING_SIZE
|
||||
#define APP_DEFAULT_NIC_TX_RING_SIZE 1024
|
||||
#endif
|
||||
|
||||
/*
|
||||
* These default values are optimized for use with the Intel(R) 82599 10 GbE
|
||||
* Controller and the DPDK ixgbe PMD. Consider using other values for other
|
||||
* network controllers and/or network drivers.
|
||||
*/
|
||||
#ifndef APP_DEFAULT_NIC_TX_PTHRESH
|
||||
#define APP_DEFAULT_NIC_TX_PTHRESH 36
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_NIC_TX_HTHRESH
|
||||
#define APP_DEFAULT_NIC_TX_HTHRESH 0
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_NIC_TX_WTHRESH
|
||||
#define APP_DEFAULT_NIC_TX_WTHRESH 0
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_NIC_TX_FREE_THRESH
|
||||
#define APP_DEFAULT_NIC_TX_FREE_THRESH 0
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_NIC_TX_RS_THRESH
|
||||
#define APP_DEFAULT_NIC_TX_RS_THRESH 0
|
||||
#endif
|
||||
|
||||
/* Software Rings */
|
||||
#ifndef APP_DEFAULT_RING_RX_SIZE
|
||||
#define APP_DEFAULT_RING_RX_SIZE 1024
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_RING_TX_SIZE
|
||||
#define APP_DEFAULT_RING_TX_SIZE 1024
|
||||
#endif
|
||||
|
||||
/* Bursts */
|
||||
#ifndef APP_MBUF_ARRAY_SIZE
|
||||
#define APP_MBUF_ARRAY_SIZE 512
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_BURST_SIZE_IO_RX_READ
|
||||
#define APP_DEFAULT_BURST_SIZE_IO_RX_READ 144
|
||||
#endif
|
||||
#if (APP_DEFAULT_BURST_SIZE_IO_RX_READ > APP_MBUF_ARRAY_SIZE)
|
||||
#error "APP_DEFAULT_BURST_SIZE_IO_RX_READ is too big"
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_BURST_SIZE_IO_RX_WRITE
|
||||
#define APP_DEFAULT_BURST_SIZE_IO_RX_WRITE 144
|
||||
#endif
|
||||
#if (APP_DEFAULT_BURST_SIZE_IO_RX_WRITE > APP_MBUF_ARRAY_SIZE)
|
||||
#error "APP_DEFAULT_BURST_SIZE_IO_RX_WRITE is too big"
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_BURST_SIZE_IO_TX_READ
|
||||
#define APP_DEFAULT_BURST_SIZE_IO_TX_READ 144
|
||||
#endif
|
||||
#if (APP_DEFAULT_BURST_SIZE_IO_TX_READ > APP_MBUF_ARRAY_SIZE)
|
||||
#error "APP_DEFAULT_BURST_SIZE_IO_TX_READ is too big"
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_BURST_SIZE_IO_TX_WRITE
|
||||
#define APP_DEFAULT_BURST_SIZE_IO_TX_WRITE 144
|
||||
#endif
|
||||
#if (APP_DEFAULT_BURST_SIZE_IO_TX_WRITE > APP_MBUF_ARRAY_SIZE)
|
||||
#error "APP_DEFAULT_BURST_SIZE_IO_TX_WRITE is too big"
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_BURST_SIZE_WORKER_READ
|
||||
#define APP_DEFAULT_BURST_SIZE_WORKER_READ 144
|
||||
#endif
|
||||
#if ((2 * APP_DEFAULT_BURST_SIZE_WORKER_READ) > APP_MBUF_ARRAY_SIZE)
|
||||
#error "APP_DEFAULT_BURST_SIZE_WORKER_READ is too big"
|
||||
#endif
|
||||
|
||||
#ifndef APP_DEFAULT_BURST_SIZE_WORKER_WRITE
|
||||
#define APP_DEFAULT_BURST_SIZE_WORKER_WRITE 144
|
||||
#endif
|
||||
#if (APP_DEFAULT_BURST_SIZE_WORKER_WRITE > APP_MBUF_ARRAY_SIZE)
|
||||
#error "APP_DEFAULT_BURST_SIZE_WORKER_WRITE is too big"
|
||||
#endif
|
||||
|
||||
/* Load balancing logic */
|
||||
#ifndef APP_DEFAULT_IO_RX_LB_POS
|
||||
#define APP_DEFAULT_IO_RX_LB_POS 29
|
||||
#endif
|
||||
#if (APP_DEFAULT_IO_RX_LB_POS >= 64)
|
||||
#error "APP_DEFAULT_IO_RX_LB_POS is too big"
|
||||
#endif
|
||||
|
||||
struct app_mbuf_array {
|
||||
struct rte_mbuf *array[APP_MBUF_ARRAY_SIZE];
|
||||
uint32_t n_mbufs;
|
||||
};
|
||||
|
||||
enum app_lcore_type {
|
||||
e_APP_LCORE_DISABLED = 0,
|
||||
e_APP_LCORE_IO,
|
||||
e_APP_LCORE_WORKER
|
||||
};
|
||||
|
||||
struct app_lcore_params_io {
|
||||
/* I/O RX */
|
||||
struct {
|
||||
/* NIC */
|
||||
struct {
|
||||
uint16_t port;
|
||||
uint8_t queue;
|
||||
} nic_queues[APP_MAX_NIC_RX_QUEUES_PER_IO_LCORE];
|
||||
uint32_t n_nic_queues;
|
||||
|
||||
/* Rings */
|
||||
struct rte_ring *rings[APP_MAX_WORKER_LCORES];
|
||||
uint32_t n_rings;
|
||||
|
||||
/* Internal buffers */
|
||||
struct app_mbuf_array mbuf_in;
|
||||
struct app_mbuf_array mbuf_out[APP_MAX_WORKER_LCORES];
|
||||
uint8_t mbuf_out_flush[APP_MAX_WORKER_LCORES];
|
||||
|
||||
/* Stats */
|
||||
uint32_t nic_queues_count[APP_MAX_NIC_RX_QUEUES_PER_IO_LCORE];
|
||||
uint32_t nic_queues_iters[APP_MAX_NIC_RX_QUEUES_PER_IO_LCORE];
|
||||
uint32_t rings_count[APP_MAX_WORKER_LCORES];
|
||||
uint32_t rings_iters[APP_MAX_WORKER_LCORES];
|
||||
} rx;
|
||||
|
||||
/* I/O TX */
|
||||
struct {
|
||||
/* Rings */
|
||||
struct rte_ring *rings[APP_MAX_NIC_PORTS][APP_MAX_WORKER_LCORES];
|
||||
|
||||
/* NIC */
|
||||
uint16_t nic_ports[APP_MAX_NIC_TX_PORTS_PER_IO_LCORE];
|
||||
uint32_t n_nic_ports;
|
||||
|
||||
/* Internal buffers */
|
||||
struct app_mbuf_array mbuf_out[APP_MAX_NIC_TX_PORTS_PER_IO_LCORE];
|
||||
uint8_t mbuf_out_flush[APP_MAX_NIC_TX_PORTS_PER_IO_LCORE];
|
||||
|
||||
/* Stats */
|
||||
uint32_t rings_count[APP_MAX_NIC_PORTS][APP_MAX_WORKER_LCORES];
|
||||
uint32_t rings_iters[APP_MAX_NIC_PORTS][APP_MAX_WORKER_LCORES];
|
||||
uint32_t nic_ports_count[APP_MAX_NIC_TX_PORTS_PER_IO_LCORE];
|
||||
uint32_t nic_ports_iters[APP_MAX_NIC_TX_PORTS_PER_IO_LCORE];
|
||||
} tx;
|
||||
};
|
||||
|
||||
struct app_lcore_params_worker {
|
||||
/* Rings */
|
||||
struct rte_ring *rings_in[APP_MAX_IO_LCORES];
|
||||
uint32_t n_rings_in;
|
||||
struct rte_ring *rings_out[APP_MAX_NIC_PORTS];
|
||||
|
||||
/* LPM table */
|
||||
struct rte_lpm *lpm_table;
|
||||
uint32_t worker_id;
|
||||
|
||||
/* Internal buffers */
|
||||
struct app_mbuf_array mbuf_in;
|
||||
struct app_mbuf_array mbuf_out[APP_MAX_NIC_PORTS];
|
||||
uint8_t mbuf_out_flush[APP_MAX_NIC_PORTS];
|
||||
|
||||
/* Stats */
|
||||
uint32_t rings_in_count[APP_MAX_IO_LCORES];
|
||||
uint32_t rings_in_iters[APP_MAX_IO_LCORES];
|
||||
uint32_t rings_out_count[APP_MAX_NIC_PORTS];
|
||||
uint32_t rings_out_iters[APP_MAX_NIC_PORTS];
|
||||
};
|
||||
|
||||
struct app_lcore_params {
|
||||
union {
|
||||
struct app_lcore_params_io io;
|
||||
struct app_lcore_params_worker worker;
|
||||
};
|
||||
enum app_lcore_type type;
|
||||
struct rte_mempool *pool;
|
||||
} __rte_cache_aligned;
|
||||
|
||||
struct app_lpm_rule {
|
||||
uint32_t ip;
|
||||
uint8_t depth;
|
||||
uint8_t if_out;
|
||||
};
|
||||
|
||||
struct app_params {
|
||||
/* lcore */
|
||||
struct app_lcore_params lcore_params[APP_MAX_LCORES];
|
||||
|
||||
/* NIC */
|
||||
uint8_t nic_rx_queue_mask[APP_MAX_NIC_PORTS][APP_MAX_RX_QUEUES_PER_NIC_PORT];
|
||||
uint8_t nic_tx_port_mask[APP_MAX_NIC_PORTS];
|
||||
|
||||
/* mbuf pools */
|
||||
struct rte_mempool *pools[APP_MAX_SOCKETS];
|
||||
|
||||
/* LPM tables */
|
||||
struct rte_lpm *lpm_tables[APP_MAX_SOCKETS];
|
||||
struct app_lpm_rule lpm_rules[APP_MAX_LPM_RULES];
|
||||
uint32_t n_lpm_rules;
|
||||
|
||||
/* rings */
|
||||
uint32_t nic_rx_ring_size;
|
||||
uint32_t nic_tx_ring_size;
|
||||
uint32_t ring_rx_size;
|
||||
uint32_t ring_tx_size;
|
||||
|
||||
/* burst size */
|
||||
uint32_t burst_size_io_rx_read;
|
||||
uint32_t burst_size_io_rx_write;
|
||||
uint32_t burst_size_io_tx_read;
|
||||
uint32_t burst_size_io_tx_write;
|
||||
uint32_t burst_size_worker_read;
|
||||
uint32_t burst_size_worker_write;
|
||||
|
||||
/* load balancing */
|
||||
uint8_t pos_lb;
|
||||
} __rte_cache_aligned;
|
||||
|
||||
extern struct app_params app;
|
||||
|
||||
int app_parse_args(int argc, char **argv);
|
||||
void app_print_usage(void);
|
||||
void app_init(void);
|
||||
int app_lcore_main_loop(void *arg);
|
||||
|
||||
int app_get_nic_rx_queues_per_port(uint16_t port);
|
||||
int app_get_lcore_for_nic_rx(uint16_t port, uint8_t queue,
|
||||
uint32_t *lcore_out);
|
||||
int app_get_lcore_for_nic_tx(uint16_t port, uint32_t *lcore_out);
|
||||
int app_is_socket_used(uint32_t socket);
|
||||
uint32_t app_get_lcores_io_rx(void);
|
||||
uint32_t app_get_lcores_worker(void);
|
||||
void app_print_params(void);
|
||||
|
||||
#endif /* _MAIN_H_ */
|
@ -1,12 +0,0 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
# Copyright(c) 2017 Intel Corporation
|
||||
|
||||
# meson file, for building this example as part of a main DPDK build.
|
||||
#
|
||||
# To build this example as a standalone application with an already-installed
|
||||
# DPDK instance, use 'make'
|
||||
|
||||
deps += 'lpm'
|
||||
sources = files(
|
||||
'config.c', 'init.c', 'main.c', 'runtime.c'
|
||||
)
|
@ -1,642 +0,0 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause
|
||||
* Copyright(c) 2010-2014 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <rte_common.h>
|
||||
#include <rte_byteorder.h>
|
||||
#include <rte_log.h>
|
||||
#include <rte_memory.h>
|
||||
#include <rte_memcpy.h>
|
||||
#include <rte_eal.h>
|
||||
#include <rte_launch.h>
|
||||
#include <rte_atomic.h>
|
||||
#include <rte_cycles.h>
|
||||
#include <rte_prefetch.h>
|
||||
#include <rte_lcore.h>
|
||||
#include <rte_per_lcore.h>
|
||||
#include <rte_branch_prediction.h>
|
||||
#include <rte_interrupts.h>
|
||||
#include <rte_random.h>
|
||||
#include <rte_debug.h>
|
||||
#include <rte_ether.h>
|
||||
#include <rte_ethdev.h>
|
||||
#include <rte_ring.h>
|
||||
#include <rte_mempool.h>
|
||||
#include <rte_mbuf.h>
|
||||
#include <rte_ip.h>
|
||||
#include <rte_tcp.h>
|
||||
#include <rte_lpm.h>
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#ifndef APP_LCORE_IO_FLUSH
|
||||
#define APP_LCORE_IO_FLUSH 1000000
|
||||
#endif
|
||||
|
||||
#ifndef APP_LCORE_WORKER_FLUSH
|
||||
#define APP_LCORE_WORKER_FLUSH 1000000
|
||||
#endif
|
||||
|
||||
#ifndef APP_STATS
|
||||
#define APP_STATS 1000000
|
||||
#endif
|
||||
|
||||
#define APP_IO_RX_DROP_ALL_PACKETS 0
|
||||
#define APP_WORKER_DROP_ALL_PACKETS 0
|
||||
#define APP_IO_TX_DROP_ALL_PACKETS 0
|
||||
|
||||
#ifndef APP_IO_RX_PREFETCH_ENABLE
|
||||
#define APP_IO_RX_PREFETCH_ENABLE 1
|
||||
#endif
|
||||
|
||||
#ifndef APP_WORKER_PREFETCH_ENABLE
|
||||
#define APP_WORKER_PREFETCH_ENABLE 1
|
||||
#endif
|
||||
|
||||
#ifndef APP_IO_TX_PREFETCH_ENABLE
|
||||
#define APP_IO_TX_PREFETCH_ENABLE 1
|
||||
#endif
|
||||
|
||||
#if APP_IO_RX_PREFETCH_ENABLE
|
||||
#define APP_IO_RX_PREFETCH0(p) rte_prefetch0(p)
|
||||
#define APP_IO_RX_PREFETCH1(p) rte_prefetch1(p)
|
||||
#else
|
||||
#define APP_IO_RX_PREFETCH0(p)
|
||||
#define APP_IO_RX_PREFETCH1(p)
|
||||
#endif
|
||||
|
||||
#if APP_WORKER_PREFETCH_ENABLE
|
||||
#define APP_WORKER_PREFETCH0(p) rte_prefetch0(p)
|
||||
#define APP_WORKER_PREFETCH1(p) rte_prefetch1(p)
|
||||
#else
|
||||
#define APP_WORKER_PREFETCH0(p)
|
||||
#define APP_WORKER_PREFETCH1(p)
|
||||
#endif
|
||||
|
||||
#if APP_IO_TX_PREFETCH_ENABLE
|
||||
#define APP_IO_TX_PREFETCH0(p) rte_prefetch0(p)
|
||||
#define APP_IO_TX_PREFETCH1(p) rte_prefetch1(p)
|
||||
#else
|
||||
#define APP_IO_TX_PREFETCH0(p)
|
||||
#define APP_IO_TX_PREFETCH1(p)
|
||||
#endif
|
||||
|
||||
static inline void
|
||||
app_lcore_io_rx_buffer_to_send (
|
||||
struct app_lcore_params_io *lp,
|
||||
uint32_t worker,
|
||||
struct rte_mbuf *mbuf,
|
||||
uint32_t bsz)
|
||||
{
|
||||
uint32_t pos;
|
||||
int ret;
|
||||
|
||||
pos = lp->rx.mbuf_out[worker].n_mbufs;
|
||||
lp->rx.mbuf_out[worker].array[pos ++] = mbuf;
|
||||
if (likely(pos < bsz)) {
|
||||
lp->rx.mbuf_out[worker].n_mbufs = pos;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = rte_ring_sp_enqueue_bulk(
|
||||
lp->rx.rings[worker],
|
||||
(void **) lp->rx.mbuf_out[worker].array,
|
||||
bsz,
|
||||
NULL);
|
||||
|
||||
if (unlikely(ret == 0)) {
|
||||
uint32_t k;
|
||||
for (k = 0; k < bsz; k ++) {
|
||||
struct rte_mbuf *m = lp->rx.mbuf_out[worker].array[k];
|
||||
rte_pktmbuf_free(m);
|
||||
}
|
||||
}
|
||||
|
||||
lp->rx.mbuf_out[worker].n_mbufs = 0;
|
||||
lp->rx.mbuf_out_flush[worker] = 0;
|
||||
|
||||
#if APP_STATS
|
||||
lp->rx.rings_iters[worker] ++;
|
||||
if (likely(ret == 0)) {
|
||||
lp->rx.rings_count[worker] ++;
|
||||
}
|
||||
if (unlikely(lp->rx.rings_iters[worker] == APP_STATS)) {
|
||||
unsigned lcore = rte_lcore_id();
|
||||
|
||||
printf("\tI/O RX %u out (worker %u): enq success rate = %.2f\n",
|
||||
lcore,
|
||||
(unsigned)worker,
|
||||
((double) lp->rx.rings_count[worker]) / ((double) lp->rx.rings_iters[worker]));
|
||||
lp->rx.rings_iters[worker] = 0;
|
||||
lp->rx.rings_count[worker] = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void
|
||||
app_lcore_io_rx(
|
||||
struct app_lcore_params_io *lp,
|
||||
uint32_t n_workers,
|
||||
uint32_t bsz_rd,
|
||||
uint32_t bsz_wr,
|
||||
uint8_t pos_lb)
|
||||
{
|
||||
struct rte_mbuf *mbuf_1_0, *mbuf_1_1, *mbuf_2_0, *mbuf_2_1;
|
||||
uint8_t *data_1_0, *data_1_1 = NULL;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < lp->rx.n_nic_queues; i ++) {
|
||||
uint16_t port = lp->rx.nic_queues[i].port;
|
||||
uint8_t queue = lp->rx.nic_queues[i].queue;
|
||||
uint32_t n_mbufs, j;
|
||||
|
||||
n_mbufs = rte_eth_rx_burst(
|
||||
port,
|
||||
queue,
|
||||
lp->rx.mbuf_in.array,
|
||||
(uint16_t) bsz_rd);
|
||||
|
||||
if (unlikely(n_mbufs == 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if APP_STATS
|
||||
lp->rx.nic_queues_iters[i] ++;
|
||||
lp->rx.nic_queues_count[i] += n_mbufs;
|
||||
if (unlikely(lp->rx.nic_queues_iters[i] == APP_STATS)) {
|
||||
struct rte_eth_stats stats;
|
||||
unsigned lcore = rte_lcore_id();
|
||||
|
||||
rte_eth_stats_get(port, &stats);
|
||||
|
||||
printf("I/O RX %u in (NIC port %u): NIC drop ratio = %.2f avg burst size = %.2f\n",
|
||||
lcore,
|
||||
port,
|
||||
(double) stats.imissed / (double) (stats.imissed + stats.ipackets),
|
||||
((double) lp->rx.nic_queues_count[i]) / ((double) lp->rx.nic_queues_iters[i]));
|
||||
lp->rx.nic_queues_iters[i] = 0;
|
||||
lp->rx.nic_queues_count[i] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if APP_IO_RX_DROP_ALL_PACKETS
|
||||
for (j = 0; j < n_mbufs; j ++) {
|
||||
struct rte_mbuf *pkt = lp->rx.mbuf_in.array[j];
|
||||
rte_pktmbuf_free(pkt);
|
||||
}
|
||||
|
||||
continue;
|
||||
#endif
|
||||
|
||||
mbuf_1_0 = lp->rx.mbuf_in.array[0];
|
||||
mbuf_1_1 = lp->rx.mbuf_in.array[1];
|
||||
data_1_0 = rte_pktmbuf_mtod(mbuf_1_0, uint8_t *);
|
||||
if (likely(n_mbufs > 1)) {
|
||||
data_1_1 = rte_pktmbuf_mtod(mbuf_1_1, uint8_t *);
|
||||
}
|
||||
|
||||
mbuf_2_0 = lp->rx.mbuf_in.array[2];
|
||||
mbuf_2_1 = lp->rx.mbuf_in.array[3];
|
||||
APP_IO_RX_PREFETCH0(mbuf_2_0);
|
||||
APP_IO_RX_PREFETCH0(mbuf_2_1);
|
||||
|
||||
for (j = 0; j + 3 < n_mbufs; j += 2) {
|
||||
struct rte_mbuf *mbuf_0_0, *mbuf_0_1;
|
||||
uint8_t *data_0_0, *data_0_1;
|
||||
uint32_t worker_0, worker_1;
|
||||
|
||||
mbuf_0_0 = mbuf_1_0;
|
||||
mbuf_0_1 = mbuf_1_1;
|
||||
data_0_0 = data_1_0;
|
||||
data_0_1 = data_1_1;
|
||||
|
||||
mbuf_1_0 = mbuf_2_0;
|
||||
mbuf_1_1 = mbuf_2_1;
|
||||
data_1_0 = rte_pktmbuf_mtod(mbuf_2_0, uint8_t *);
|
||||
data_1_1 = rte_pktmbuf_mtod(mbuf_2_1, uint8_t *);
|
||||
APP_IO_RX_PREFETCH0(data_1_0);
|
||||
APP_IO_RX_PREFETCH0(data_1_1);
|
||||
|
||||
mbuf_2_0 = lp->rx.mbuf_in.array[j+4];
|
||||
mbuf_2_1 = lp->rx.mbuf_in.array[j+5];
|
||||
APP_IO_RX_PREFETCH0(mbuf_2_0);
|
||||
APP_IO_RX_PREFETCH0(mbuf_2_1);
|
||||
|
||||
worker_0 = data_0_0[pos_lb] & (n_workers - 1);
|
||||
worker_1 = data_0_1[pos_lb] & (n_workers - 1);
|
||||
|
||||
app_lcore_io_rx_buffer_to_send(lp, worker_0, mbuf_0_0, bsz_wr);
|
||||
app_lcore_io_rx_buffer_to_send(lp, worker_1, mbuf_0_1, bsz_wr);
|
||||
}
|
||||
|
||||
/* Handle the last 1, 2 (when n_mbufs is even) or 3 (when n_mbufs is odd) packets */
|
||||
for ( ; j < n_mbufs; j += 1) {
|
||||
struct rte_mbuf *mbuf;
|
||||
uint8_t *data;
|
||||
uint32_t worker;
|
||||
|
||||
mbuf = mbuf_1_0;
|
||||
mbuf_1_0 = mbuf_1_1;
|
||||
mbuf_1_1 = mbuf_2_0;
|
||||
mbuf_2_0 = mbuf_2_1;
|
||||
|
||||
data = rte_pktmbuf_mtod(mbuf, uint8_t *);
|
||||
|
||||
APP_IO_RX_PREFETCH0(mbuf_1_0);
|
||||
|
||||
worker = data[pos_lb] & (n_workers - 1);
|
||||
|
||||
app_lcore_io_rx_buffer_to_send(lp, worker, mbuf, bsz_wr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
app_lcore_io_rx_flush(struct app_lcore_params_io *lp, uint32_t n_workers)
|
||||
{
|
||||
uint32_t worker;
|
||||
|
||||
for (worker = 0; worker < n_workers; worker ++) {
|
||||
int ret;
|
||||
|
||||
if (likely((lp->rx.mbuf_out_flush[worker] == 0) ||
|
||||
(lp->rx.mbuf_out[worker].n_mbufs == 0))) {
|
||||
lp->rx.mbuf_out_flush[worker] = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = rte_ring_sp_enqueue_bulk(
|
||||
lp->rx.rings[worker],
|
||||
(void **) lp->rx.mbuf_out[worker].array,
|
||||
lp->rx.mbuf_out[worker].n_mbufs,
|
||||
NULL);
|
||||
|
||||
if (unlikely(ret == 0)) {
|
||||
uint32_t k;
|
||||
for (k = 0; k < lp->rx.mbuf_out[worker].n_mbufs; k ++) {
|
||||
struct rte_mbuf *pkt_to_free = lp->rx.mbuf_out[worker].array[k];
|
||||
rte_pktmbuf_free(pkt_to_free);
|
||||
}
|
||||
}
|
||||
|
||||
lp->rx.mbuf_out[worker].n_mbufs = 0;
|
||||
lp->rx.mbuf_out_flush[worker] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
app_lcore_io_tx(
|
||||
struct app_lcore_params_io *lp,
|
||||
uint32_t n_workers,
|
||||
uint32_t bsz_rd,
|
||||
uint32_t bsz_wr)
|
||||
{
|
||||
uint32_t worker;
|
||||
|
||||
for (worker = 0; worker < n_workers; worker ++) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < lp->tx.n_nic_ports; i ++) {
|
||||
uint16_t port = lp->tx.nic_ports[i];
|
||||
struct rte_ring *ring = lp->tx.rings[port][worker];
|
||||
uint32_t n_mbufs, n_pkts;
|
||||
int ret;
|
||||
|
||||
n_mbufs = lp->tx.mbuf_out[port].n_mbufs;
|
||||
ret = rte_ring_sc_dequeue_bulk(
|
||||
ring,
|
||||
(void **) &lp->tx.mbuf_out[port].array[n_mbufs],
|
||||
bsz_rd,
|
||||
NULL);
|
||||
|
||||
if (unlikely(ret == 0))
|
||||
continue;
|
||||
|
||||
n_mbufs += bsz_rd;
|
||||
|
||||
#if APP_IO_TX_DROP_ALL_PACKETS
|
||||
{
|
||||
uint32_t j;
|
||||
APP_IO_TX_PREFETCH0(lp->tx.mbuf_out[port].array[0]);
|
||||
APP_IO_TX_PREFETCH0(lp->tx.mbuf_out[port].array[1]);
|
||||
|
||||
for (j = 0; j < n_mbufs; j ++) {
|
||||
if (likely(j < n_mbufs - 2)) {
|
||||
APP_IO_TX_PREFETCH0(lp->tx.mbuf_out[port].array[j + 2]);
|
||||
}
|
||||
|
||||
rte_pktmbuf_free(lp->tx.mbuf_out[port].array[j]);
|
||||
}
|
||||
|
||||
lp->tx.mbuf_out[port].n_mbufs = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(n_mbufs < bsz_wr)) {
|
||||
lp->tx.mbuf_out[port].n_mbufs = n_mbufs;
|
||||
continue;
|
||||
}
|
||||
|
||||
n_pkts = rte_eth_tx_burst(
|
||||
port,
|
||||
0,
|
||||
lp->tx.mbuf_out[port].array,
|
||||
(uint16_t) n_mbufs);
|
||||
|
||||
#if APP_STATS
|
||||
lp->tx.nic_ports_iters[port] ++;
|
||||
lp->tx.nic_ports_count[port] += n_pkts;
|
||||
if (unlikely(lp->tx.nic_ports_iters[port] == APP_STATS)) {
|
||||
unsigned lcore = rte_lcore_id();
|
||||
|
||||
printf("\t\t\tI/O TX %u out (port %u): avg burst size = %.2f\n",
|
||||
lcore,
|
||||
port,
|
||||
((double) lp->tx.nic_ports_count[port]) / ((double) lp->tx.nic_ports_iters[port]));
|
||||
lp->tx.nic_ports_iters[port] = 0;
|
||||
lp->tx.nic_ports_count[port] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(n_pkts < n_mbufs)) {
|
||||
uint32_t k;
|
||||
for (k = n_pkts; k < n_mbufs; k ++) {
|
||||
struct rte_mbuf *pkt_to_free = lp->tx.mbuf_out[port].array[k];
|
||||
rte_pktmbuf_free(pkt_to_free);
|
||||
}
|
||||
}
|
||||
lp->tx.mbuf_out[port].n_mbufs = 0;
|
||||
lp->tx.mbuf_out_flush[port] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
app_lcore_io_tx_flush(struct app_lcore_params_io *lp)
|
||||
{
|
||||
uint16_t port;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < lp->tx.n_nic_ports; i++) {
|
||||
uint32_t n_pkts;
|
||||
|
||||
port = lp->tx.nic_ports[i];
|
||||
if (likely((lp->tx.mbuf_out_flush[port] == 0) ||
|
||||
(lp->tx.mbuf_out[port].n_mbufs == 0))) {
|
||||
lp->tx.mbuf_out_flush[port] = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
n_pkts = rte_eth_tx_burst(
|
||||
port,
|
||||
0,
|
||||
lp->tx.mbuf_out[port].array,
|
||||
(uint16_t) lp->tx.mbuf_out[port].n_mbufs);
|
||||
|
||||
if (unlikely(n_pkts < lp->tx.mbuf_out[port].n_mbufs)) {
|
||||
uint32_t k;
|
||||
for (k = n_pkts; k < lp->tx.mbuf_out[port].n_mbufs; k ++) {
|
||||
struct rte_mbuf *pkt_to_free = lp->tx.mbuf_out[port].array[k];
|
||||
rte_pktmbuf_free(pkt_to_free);
|
||||
}
|
||||
}
|
||||
|
||||
lp->tx.mbuf_out[port].n_mbufs = 0;
|
||||
lp->tx.mbuf_out_flush[port] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
app_lcore_main_loop_io(void)
|
||||
{
|
||||
uint32_t lcore = rte_lcore_id();
|
||||
struct app_lcore_params_io *lp = &app.lcore_params[lcore].io;
|
||||
uint32_t n_workers = app_get_lcores_worker();
|
||||
uint64_t i = 0;
|
||||
|
||||
uint32_t bsz_rx_rd = app.burst_size_io_rx_read;
|
||||
uint32_t bsz_rx_wr = app.burst_size_io_rx_write;
|
||||
uint32_t bsz_tx_rd = app.burst_size_io_tx_read;
|
||||
uint32_t bsz_tx_wr = app.burst_size_io_tx_write;
|
||||
|
||||
uint8_t pos_lb = app.pos_lb;
|
||||
|
||||
for ( ; ; ) {
|
||||
if (APP_LCORE_IO_FLUSH && (unlikely(i == APP_LCORE_IO_FLUSH))) {
|
||||
if (likely(lp->rx.n_nic_queues > 0)) {
|
||||
app_lcore_io_rx_flush(lp, n_workers);
|
||||
}
|
||||
|
||||
if (likely(lp->tx.n_nic_ports > 0)) {
|
||||
app_lcore_io_tx_flush(lp);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
|
||||
if (likely(lp->rx.n_nic_queues > 0)) {
|
||||
app_lcore_io_rx(lp, n_workers, bsz_rx_rd, bsz_rx_wr, pos_lb);
|
||||
}
|
||||
|
||||
if (likely(lp->tx.n_nic_ports > 0)) {
|
||||
app_lcore_io_tx(lp, n_workers, bsz_tx_rd, bsz_tx_wr);
|
||||
}
|
||||
|
||||
i ++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
app_lcore_worker(
|
||||
struct app_lcore_params_worker *lp,
|
||||
uint32_t bsz_rd,
|
||||
uint32_t bsz_wr)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < lp->n_rings_in; i ++) {
|
||||
struct rte_ring *ring_in = lp->rings_in[i];
|
||||
uint32_t j;
|
||||
int ret;
|
||||
|
||||
ret = rte_ring_sc_dequeue_bulk(
|
||||
ring_in,
|
||||
(void **) lp->mbuf_in.array,
|
||||
bsz_rd,
|
||||
NULL);
|
||||
|
||||
if (unlikely(ret == 0))
|
||||
continue;
|
||||
|
||||
#if APP_WORKER_DROP_ALL_PACKETS
|
||||
for (j = 0; j < bsz_rd; j ++) {
|
||||
struct rte_mbuf *pkt = lp->mbuf_in.array[j];
|
||||
rte_pktmbuf_free(pkt);
|
||||
}
|
||||
|
||||
continue;
|
||||
#endif
|
||||
|
||||
APP_WORKER_PREFETCH1(rte_pktmbuf_mtod(lp->mbuf_in.array[0], unsigned char *));
|
||||
APP_WORKER_PREFETCH0(lp->mbuf_in.array[1]);
|
||||
|
||||
for (j = 0; j < bsz_rd; j ++) {
|
||||
struct rte_mbuf *pkt;
|
||||
struct rte_ipv4_hdr *ipv4_hdr;
|
||||
uint32_t ipv4_dst, pos;
|
||||
uint32_t port;
|
||||
|
||||
if (likely(j < bsz_rd - 1)) {
|
||||
APP_WORKER_PREFETCH1(rte_pktmbuf_mtod(lp->mbuf_in.array[j+1], unsigned char *));
|
||||
}
|
||||
if (likely(j < bsz_rd - 2)) {
|
||||
APP_WORKER_PREFETCH0(lp->mbuf_in.array[j+2]);
|
||||
}
|
||||
|
||||
pkt = lp->mbuf_in.array[j];
|
||||
ipv4_hdr = rte_pktmbuf_mtod_offset(
|
||||
pkt, struct rte_ipv4_hdr *,
|
||||
sizeof(struct rte_ether_hdr));
|
||||
ipv4_dst = rte_be_to_cpu_32(ipv4_hdr->dst_addr);
|
||||
|
||||
if (unlikely(rte_lpm_lookup(lp->lpm_table, ipv4_dst, &port) != 0)) {
|
||||
port = pkt->port;
|
||||
}
|
||||
|
||||
pos = lp->mbuf_out[port].n_mbufs;
|
||||
|
||||
lp->mbuf_out[port].array[pos ++] = pkt;
|
||||
if (likely(pos < bsz_wr)) {
|
||||
lp->mbuf_out[port].n_mbufs = pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = rte_ring_sp_enqueue_bulk(
|
||||
lp->rings_out[port],
|
||||
(void **) lp->mbuf_out[port].array,
|
||||
bsz_wr,
|
||||
NULL);
|
||||
|
||||
#if APP_STATS
|
||||
lp->rings_out_iters[port] ++;
|
||||
if (ret > 0) {
|
||||
lp->rings_out_count[port] += 1;
|
||||
}
|
||||
if (lp->rings_out_iters[port] == APP_STATS){
|
||||
printf("\t\tWorker %u out (NIC port %u): enq success rate = %.2f\n",
|
||||
(unsigned) lp->worker_id,
|
||||
port,
|
||||
((double) lp->rings_out_count[port]) / ((double) lp->rings_out_iters[port]));
|
||||
lp->rings_out_iters[port] = 0;
|
||||
lp->rings_out_count[port] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(ret == 0)) {
|
||||
uint32_t k;
|
||||
for (k = 0; k < bsz_wr; k ++) {
|
||||
struct rte_mbuf *pkt_to_free = lp->mbuf_out[port].array[k];
|
||||
rte_pktmbuf_free(pkt_to_free);
|
||||
}
|
||||
}
|
||||
|
||||
lp->mbuf_out[port].n_mbufs = 0;
|
||||
lp->mbuf_out_flush[port] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
app_lcore_worker_flush(struct app_lcore_params_worker *lp)
|
||||
{
|
||||
uint32_t port;
|
||||
|
||||
for (port = 0; port < APP_MAX_NIC_PORTS; port ++) {
|
||||
int ret;
|
||||
|
||||
if (unlikely(lp->rings_out[port] == NULL)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (likely((lp->mbuf_out_flush[port] == 0) ||
|
||||
(lp->mbuf_out[port].n_mbufs == 0))) {
|
||||
lp->mbuf_out_flush[port] = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = rte_ring_sp_enqueue_bulk(
|
||||
lp->rings_out[port],
|
||||
(void **) lp->mbuf_out[port].array,
|
||||
lp->mbuf_out[port].n_mbufs,
|
||||
NULL);
|
||||
|
||||
if (unlikely(ret == 0)) {
|
||||
uint32_t k;
|
||||
for (k = 0; k < lp->mbuf_out[port].n_mbufs; k ++) {
|
||||
struct rte_mbuf *pkt_to_free = lp->mbuf_out[port].array[k];
|
||||
rte_pktmbuf_free(pkt_to_free);
|
||||
}
|
||||
}
|
||||
|
||||
lp->mbuf_out[port].n_mbufs = 0;
|
||||
lp->mbuf_out_flush[port] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
app_lcore_main_loop_worker(void) {
|
||||
uint32_t lcore = rte_lcore_id();
|
||||
struct app_lcore_params_worker *lp = &app.lcore_params[lcore].worker;
|
||||
uint64_t i = 0;
|
||||
|
||||
uint32_t bsz_rd = app.burst_size_worker_read;
|
||||
uint32_t bsz_wr = app.burst_size_worker_write;
|
||||
|
||||
for ( ; ; ) {
|
||||
if (APP_LCORE_WORKER_FLUSH && (unlikely(i == APP_LCORE_WORKER_FLUSH))) {
|
||||
app_lcore_worker_flush(lp);
|
||||
i = 0;
|
||||
}
|
||||
|
||||
app_lcore_worker(lp, bsz_rd, bsz_wr);
|
||||
|
||||
i ++;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
app_lcore_main_loop(__attribute__((unused)) void *arg)
|
||||
{
|
||||
struct app_lcore_params *lp;
|
||||
unsigned lcore;
|
||||
|
||||
lcore = rte_lcore_id();
|
||||
lp = &app.lcore_params[lcore];
|
||||
|
||||
if (lp->type == e_APP_LCORE_IO) {
|
||||
printf("Logical core %u (I/O) main loop.\n", lcore);
|
||||
app_lcore_main_loop_io();
|
||||
}
|
||||
|
||||
if (lp->type == e_APP_LCORE_WORKER) {
|
||||
printf("Logical core %u (worker %u) main loop.\n",
|
||||
lcore,
|
||||
(unsigned) lp->worker.worker_id);
|
||||
app_lcore_main_loop_worker();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -24,7 +24,6 @@ all_examples = [
|
||||
'l2fwd-keepalive', 'l3fwd',
|
||||
'l3fwd-acl', 'l3fwd-power',
|
||||
'link_status_interrupt',
|
||||
'load_balancer',
|
||||
'multi_process/client_server_mp/mp_client',
|
||||
'multi_process/client_server_mp/mp_server',
|
||||
'multi_process/hotplug_mp',
|
||||
|
Loading…
x
Reference in New Issue
Block a user