1.. SPDX-License-Identifier: BSD-3-Clause 2 Copyright(c) 2015 Intel Corporation. 3 4Basic Forwarding Sample Application 5=================================== 6 7The Basic Forwarding sample application is a simple *skeleton* example of a 8forwarding application. 9 10It is intended as a demonstration of the basic components of a DPDK forwarding 11application. For more detailed implementations see the L2 and L3 forwarding 12sample applications. 13 14Compiling the Application 15------------------------- 16 17To compile the sample application see :doc:`compiling`. 18 19The application is located in the ``skeleton`` sub-directory. 20 21Running the Application 22----------------------- 23 24To run the example in a ``linux`` environment: 25 26.. code-block:: console 27 28 ./build/basicfwd -l 1 -n 4 29 30Refer to *DPDK Getting Started Guide* for general information on running 31applications and the Environment Abstraction Layer (EAL) options. 32 33 34Explanation 35----------- 36 37The following sections provide an explanation of the main components of the 38code. 39 40All DPDK library functions used in the sample code are prefixed with ``rte_`` 41and are explained in detail in the *DPDK API Documentation*. 42 43 44The Main Function 45~~~~~~~~~~~~~~~~~ 46 47The ``main()`` function performs the initialization and calls the execution 48threads for each lcore. 49 50The first task is to initialize the Environment Abstraction Layer (EAL). The 51``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()`` 52function. The value returned is the number of parsed arguments: 53 54.. code-block:: c 55 56 int ret = rte_eal_init(argc, argv); 57 if (ret < 0) 58 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); 59 60 61The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers) 62used by the application: 63 64.. code-block:: c 65 66 mbuf_pool = rte_mempool_create("MBUF_POOL", 67 NUM_MBUFS * nb_ports, 68 MBUF_SIZE, 69 MBUF_CACHE_SIZE, 70 sizeof(struct rte_pktmbuf_pool_private), 71 rte_pktmbuf_pool_init, NULL, 72 rte_pktmbuf_init, NULL, 73 rte_socket_id(), 74 0); 75 76Mbufs are the packet buffer structure used by DPDK. They are explained in 77detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*. 78 79The ``main()`` function also initializes all the ports using the user defined 80``port_init()`` function which is explained in the next section: 81 82.. code-block:: c 83 84 RTE_ETH_FOREACH_DEV(portid) { 85 if (port_init(portid, mbuf_pool) != 0) { 86 rte_exit(EXIT_FAILURE, 87 "Cannot init port %" PRIu8 "\n", portid); 88 } 89 } 90 91 92Once the initialization is complete, the application is ready to launch a 93function on an lcore. In this example ``lcore_main()`` is called on a single 94lcore. 95 96 97.. code-block:: c 98 99 lcore_main(); 100 101The ``lcore_main()`` function is explained below. 102 103 104 105The Port Initialization Function 106~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 107 108The main functional part of the port initialization used in the Basic 109Forwarding application is shown below: 110 111.. code-block:: c 112 113 static inline int 114 port_init(uint16_t port, struct rte_mempool *mbuf_pool) 115 { 116 struct rte_eth_conf port_conf = port_conf_default; 117 const uint16_t rx_rings = 1, tx_rings = 1; 118 struct rte_ether_addr addr; 119 int retval; 120 uint16_t q; 121 122 if (!rte_eth_dev_is_valid_port(port)) 123 return -1; 124 125 /* Configure the Ethernet device. */ 126 retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf); 127 if (retval != 0) 128 return retval; 129 130 /* Allocate and set up 1 RX queue per Ethernet port. */ 131 for (q = 0; q < rx_rings; q++) { 132 retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE, 133 rte_eth_dev_socket_id(port), NULL, mbuf_pool); 134 if (retval < 0) 135 return retval; 136 } 137 138 /* Allocate and set up 1 TX queue per Ethernet port. */ 139 for (q = 0; q < tx_rings; q++) { 140 retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE, 141 rte_eth_dev_socket_id(port), NULL); 142 if (retval < 0) 143 return retval; 144 } 145 146 /* Start the Ethernet port. */ 147 retval = rte_eth_dev_start(port); 148 if (retval < 0) 149 return retval; 150 151 /* Enable RX in promiscuous mode for the Ethernet device. */ 152 rte_eth_promiscuous_enable(port); 153 154 return 0; 155 } 156 157The Ethernet ports are configured with default settings using the 158``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct: 159 160.. code-block:: c 161 162 static const struct rte_eth_conf port_conf_default = { 163 .rxmode = { .max_rx_pkt_len = RTE_ETHER_MAX_LEN } 164 }; 165 166For this example the ports are set up with 1 RX and 1 TX queue using the 167``rte_eth_rx_queue_setup()`` and ``rte_eth_tx_queue_setup()`` functions. 168 169The Ethernet port is then started: 170 171.. code-block:: c 172 173 retval = rte_eth_dev_start(port); 174 175 176Finally the RX port is set in promiscuous mode: 177 178.. code-block:: c 179 180 rte_eth_promiscuous_enable(port); 181 182 183The Lcores Main 184~~~~~~~~~~~~~~~ 185 186As we saw above the ``main()`` function calls an application function on the 187available lcores. For the Basic Forwarding application the lcore function 188looks like the following: 189 190.. code-block:: c 191 192 static __attribute__((noreturn)) void 193 lcore_main(void) 194 { 195 uint16_t port; 196 197 /* 198 * Check that the port is on the same NUMA node as the polling thread 199 * for best performance. 200 */ 201 RTE_ETH_FOREACH_DEV(port) 202 if (rte_eth_dev_socket_id(port) > 0 && 203 rte_eth_dev_socket_id(port) != 204 (int)rte_socket_id()) 205 printf("WARNING, port %u is on remote NUMA node to " 206 "polling thread.\n\tPerformance will " 207 "not be optimal.\n", port); 208 209 printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n", 210 rte_lcore_id()); 211 212 /* Run until the application is quit or killed. */ 213 for (;;) { 214 /* 215 * Receive packets on a port and forward them on the paired 216 * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc. 217 */ 218 RTE_ETH_FOREACH_DEV(port) { 219 220 /* Get burst of RX packets, from first port of pair. */ 221 struct rte_mbuf *bufs[BURST_SIZE]; 222 const uint16_t nb_rx = rte_eth_rx_burst(port, 0, 223 bufs, BURST_SIZE); 224 225 if (unlikely(nb_rx == 0)) 226 continue; 227 228 /* Send burst of TX packets, to second port of pair. */ 229 const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0, 230 bufs, nb_rx); 231 232 /* Free any unsent packets. */ 233 if (unlikely(nb_tx < nb_rx)) { 234 uint16_t buf; 235 for (buf = nb_tx; buf < nb_rx; buf++) 236 rte_pktmbuf_free(bufs[buf]); 237 } 238 } 239 } 240 } 241 242 243The main work of the application is done within the loop: 244 245.. code-block:: c 246 247 for (;;) { 248 RTE_ETH_FOREACH_DEV(port) { 249 250 /* Get burst of RX packets, from first port of pair. */ 251 struct rte_mbuf *bufs[BURST_SIZE]; 252 const uint16_t nb_rx = rte_eth_rx_burst(port, 0, 253 bufs, BURST_SIZE); 254 255 if (unlikely(nb_rx == 0)) 256 continue; 257 258 /* Send burst of TX packets, to second port of pair. */ 259 const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0, 260 bufs, nb_rx); 261 262 /* Free any unsent packets. */ 263 if (unlikely(nb_tx < nb_rx)) { 264 uint16_t buf; 265 for (buf = nb_tx; buf < nb_rx; buf++) 266 rte_pktmbuf_free(bufs[buf]); 267 } 268 } 269 } 270 271Packets are received in bursts on the RX ports and transmitted in bursts on 272the TX ports. The ports are grouped in pairs with a simple mapping scheme 273using the an XOR on the port number:: 274 275 0 -> 1 276 1 -> 0 277 278 2 -> 3 279 3 -> 2 280 281 etc. 282 283The ``rte_eth_tx_burst()`` function frees the memory buffers of packets that 284are transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they 285must be freed explicitly using ``rte_pktmbuf_free()``. 286 287The forwarding loop can be interrupted and the application closed using 288``Ctrl-C``. 289