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