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 42 43Compiling the Application 44------------------------- 45 46To compile the application export the path to the DPDK source tree and go to 47the example directory: 48 49.. code-block:: console 50 51 export RTE_SDK=/path/to/rte_sdk 52 53 cd ${RTE_SDK}/examples/skeleton 54 55Set the target, for example: 56 57.. code-block:: console 58 59 export RTE_TARGET=x86_64-native-linuxapp-gcc 60 61See the *DPDK Getting Started* Guide for possible ``RTE_TARGET`` values. 62 63Build the application as follows: 64 65.. code-block:: console 66 67 make 68 69 70Running the Application 71----------------------- 72 73To run the example in a ``linuxapp`` environment: 74 75.. code-block:: console 76 77 ./build/basicfwd -l 1 -n 4 78 79Refer to *DPDK Getting Started Guide* for general information on running 80applications and the Environment Abstraction Layer (EAL) options. 81 82 83Explanation 84----------- 85 86The following sections provide an explanation of the main components of the 87code. 88 89All DPDK library functions used in the sample code are prefixed with ``rte_`` 90and are explained in detail in the *DPDK API Documentation*. 91 92 93The Main Function 94~~~~~~~~~~~~~~~~~ 95 96The ``main()`` function performs the initialization and calls the execution 97threads for each lcore. 98 99The first task is to initialize the Environment Abstraction Layer (EAL). The 100``argc`` and ``argv`` arguments are provided to the ``rte_eal_init()`` 101function. The value returned is the number of parsed arguments: 102 103.. code-block:: c 104 105 int ret = rte_eal_init(argc, argv); 106 if (ret < 0) 107 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); 108 109 110The ``main()`` also allocates a mempool to hold the mbufs (Message Buffers) 111used by the application: 112 113.. code-block:: c 114 115 mbuf_pool = rte_mempool_create("MBUF_POOL", 116 NUM_MBUFS * nb_ports, 117 MBUF_SIZE, 118 MBUF_CACHE_SIZE, 119 sizeof(struct rte_pktmbuf_pool_private), 120 rte_pktmbuf_pool_init, NULL, 121 rte_pktmbuf_init, NULL, 122 rte_socket_id(), 123 0); 124 125Mbufs are the packet buffer structure used by DPDK. They are explained in 126detail in the "Mbuf Library" section of the *DPDK Programmer's Guide*. 127 128The ``main()`` function also initializes all the ports using the user defined 129``port_init()`` function which is explained in the next section: 130 131.. code-block:: c 132 133 for (portid = 0; portid < nb_ports; portid++) { 134 if (port_init(portid, mbuf_pool) != 0) { 135 rte_exit(EXIT_FAILURE, 136 "Cannot init port %" PRIu8 "\n", portid); 137 } 138 } 139 140 141Once the initialization is complete, the application is ready to launch a 142function on an lcore. In this example ``lcore_main()`` is called on a single 143lcore. 144 145 146.. code-block:: c 147 148 lcore_main(); 149 150The ``lcore_main()`` function is explained below. 151 152 153 154The Port Initialization Function 155~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 156 157The main functional part of the port initialization used in the Basic 158Forwarding application is shown below: 159 160.. code-block:: c 161 162 static inline int 163 port_init(uint16_t port, struct rte_mempool *mbuf_pool) 164 { 165 struct rte_eth_conf port_conf = port_conf_default; 166 const uint16_t rx_rings = 1, tx_rings = 1; 167 struct ether_addr addr; 168 int retval; 169 uint16_t q; 170 171 if (port >= rte_eth_dev_count()) 172 return -1; 173 174 /* Configure the Ethernet device. */ 175 retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf); 176 if (retval != 0) 177 return retval; 178 179 /* Allocate and set up 1 RX queue per Ethernet port. */ 180 for (q = 0; q < rx_rings; q++) { 181 retval = rte_eth_rx_queue_setup(port, q, RX_RING_SIZE, 182 rte_eth_dev_socket_id(port), NULL, mbuf_pool); 183 if (retval < 0) 184 return retval; 185 } 186 187 /* Allocate and set up 1 TX queue per Ethernet port. */ 188 for (q = 0; q < tx_rings; q++) { 189 retval = rte_eth_tx_queue_setup(port, q, TX_RING_SIZE, 190 rte_eth_dev_socket_id(port), NULL); 191 if (retval < 0) 192 return retval; 193 } 194 195 /* Start the Ethernet port. */ 196 retval = rte_eth_dev_start(port); 197 if (retval < 0) 198 return retval; 199 200 /* Enable RX in promiscuous mode for the Ethernet device. */ 201 rte_eth_promiscuous_enable(port); 202 203 return 0; 204 } 205 206The Ethernet ports are configured with default settings using the 207``rte_eth_dev_configure()`` function and the ``port_conf_default`` struct: 208 209.. code-block:: c 210 211 static const struct rte_eth_conf port_conf_default = { 212 .rxmode = { .max_rx_pkt_len = ETHER_MAX_LEN } 213 }; 214 215For this example the ports are set up with 1 RX and 1 TX queue using the 216``rte_eth_rx_queue_setup()`` and ``rte_eth_tx_queue_setup()`` functions. 217 218The Ethernet port is then started: 219 220.. code-block:: c 221 222 retval = rte_eth_dev_start(port); 223 224 225Finally the RX port is set in promiscuous mode: 226 227.. code-block:: c 228 229 rte_eth_promiscuous_enable(port); 230 231 232The Lcores Main 233~~~~~~~~~~~~~~~ 234 235As we saw above the ``main()`` function calls an application function on the 236available lcores. For the Basic Forwarding application the lcore function 237looks like the following: 238 239.. code-block:: c 240 241 static __attribute__((noreturn)) void 242 lcore_main(void) 243 { 244 const uint16_t nb_ports = rte_eth_dev_count(); 245 uint16_t port; 246 247 /* 248 * Check that the port is on the same NUMA node as the polling thread 249 * for best performance. 250 */ 251 for (port = 0; port < nb_ports; port++) 252 if (rte_eth_dev_socket_id(port) > 0 && 253 rte_eth_dev_socket_id(port) != 254 (int)rte_socket_id()) 255 printf("WARNING, port %u is on remote NUMA node to " 256 "polling thread.\n\tPerformance will " 257 "not be optimal.\n", port); 258 259 printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n", 260 rte_lcore_id()); 261 262 /* Run until the application is quit or killed. */ 263 for (;;) { 264 /* 265 * Receive packets on a port and forward them on the paired 266 * port. The mapping is 0 -> 1, 1 -> 0, 2 -> 3, 3 -> 2, etc. 267 */ 268 for (port = 0; port < nb_ports; port++) { 269 270 /* Get burst of RX packets, from first port of pair. */ 271 struct rte_mbuf *bufs[BURST_SIZE]; 272 const uint16_t nb_rx = rte_eth_rx_burst(port, 0, 273 bufs, BURST_SIZE); 274 275 if (unlikely(nb_rx == 0)) 276 continue; 277 278 /* Send burst of TX packets, to second port of pair. */ 279 const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0, 280 bufs, nb_rx); 281 282 /* Free any unsent packets. */ 283 if (unlikely(nb_tx < nb_rx)) { 284 uint16_t buf; 285 for (buf = nb_tx; buf < nb_rx; buf++) 286 rte_pktmbuf_free(bufs[buf]); 287 } 288 } 289 } 290 } 291 292 293The main work of the application is done within the loop: 294 295.. code-block:: c 296 297 for (;;) { 298 for (port = 0; port < nb_ports; port++) { 299 300 /* Get burst of RX packets, from first port of pair. */ 301 struct rte_mbuf *bufs[BURST_SIZE]; 302 const uint16_t nb_rx = rte_eth_rx_burst(port, 0, 303 bufs, BURST_SIZE); 304 305 if (unlikely(nb_rx == 0)) 306 continue; 307 308 /* Send burst of TX packets, to second port of pair. */ 309 const uint16_t nb_tx = rte_eth_tx_burst(port ^ 1, 0, 310 bufs, nb_rx); 311 312 /* Free any unsent packets. */ 313 if (unlikely(nb_tx < nb_rx)) { 314 uint16_t buf; 315 for (buf = nb_tx; buf < nb_rx; buf++) 316 rte_pktmbuf_free(bufs[buf]); 317 } 318 } 319 } 320 321Packets are received in bursts on the RX ports and transmitted in bursts on 322the TX ports. The ports are grouped in pairs with a simple mapping scheme 323using the an XOR on the port number:: 324 325 0 -> 1 326 1 -> 0 327 328 2 -> 3 329 3 -> 2 330 331 etc. 332 333The ``rte_eth_tx_burst()`` function frees the memory buffers of packets that 334are transmitted. If packets fail to transmit, ``(nb_tx < nb_rx)``, then they 335must be freed explicitly using ``rte_pktmbuf_free()``. 336 337The forwarding loop can be interrupted and the application closed using 338``Ctrl-C``. 339